Blood Glucose Level Tracker

Blood Glucose Level Tracker

Log your readings, track trends, and gain insights.

Your Glucose Snapshot

7-Day Average

--

mg/dL

7-Day Low

--

mg/dL

7-Day High

--

mg/dL

Log a New Reading

Complete Log History

Your saved readings will appear here.

${entry.context}

${entry.value} mg/dL

${entry.notes ? `

Notes: ${entry.notes}

` : ''}
`).join(''); }; const renderDashboard = () => { const sevenDaysAgo = new Date(); sevenDaysAgo.setDate(sevenDaysAgo.getDate() - 7); const recentEntries = logEntries.filter(e => new Date(e.datetime) >= sevenDaysAgo); if (recentEntries.length > 0) { const values = recentEntries.map(e => e.value); avgReadingEl.textContent = (values.reduce((a, b) => a + b, 0) / values.length).toFixed(0); lowReadingEl.textContent = Math.min(...values); highReadingEl.textContent = Math.max(...values); } else { avgReadingEl.textContent = '--'; lowReadingEl.textContent = '--'; highReadingEl.textContent = '--'; } // Chart.js rendering const ctx = document.getElementById('glucoseChart').getContext('2d'); if (glucoseChart) { glucoseChart.destroy(); } glucoseChart = new Chart(ctx, { type: 'line', data: { labels: recentEntries.map(e => formatDateTime(e.datetime)), datasets: [{ label: 'Glucose (mg/dL)', data: recentEntries.map(e => e.value), borderColor: '#4f46e5', backgroundColor: 'rgba(79, 70, 229, 0.1)', fill: true, tension: 0.1 }] }, options: { responsive: true, plugins: { legend: { display: false }, tooltip: { callbacks: { footer: (tooltipItems) => { const entry = recentEntries[tooltipItems[0].dataIndex]; return `Context: ${entry.context}`; } } } }, scales: { x: { display: false }, y: { beginAtZero: false } } } }); }; // --- EVENT LISTENERS --- const attachEventListeners = () => { tabs.forEach(tab => tab.addEventListener('click', () => switchTab(tab.dataset.tab))); prevBtn.addEventListener('click', navigatePrev); nextBtn.addEventListener('click', navigateNext); addEntryBtn.addEventListener('click', handleAddEntry); getInsightsBtn.addEventListener('click', handleGetInsights); 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); }; // --- DATA HANDLING --- const handleAddEntry = () => { const value = parseInt(glucoseValueInput.value); if (!readingDateInput.value || !readingTimeInput.value || !value) { alert("Please fill in the date, time, and glucose level."); return; } const entry = { datetime: `${readingDateInput.value}T${readingTimeInput.value}`, value: value, context: readingContextSelect.value, notes: readingNotesInput.value, }; logEntries.push(entry); saveToLocalStorage(); renderAll(); alert("Reading saved successfully!"); switchTab('dashboard'); }; const saveToLocalStorage = () => { localStorage.setItem('glucoseTrackerEntries', JSON.stringify(logEntries)); }; const loadFromLocalStorage = () => { const saved = localStorage.getItem('glucoseTrackerEntries'); if (saved) { logEntries = JSON.parse(saved); } }; // --- API & INSIGHTS LOGIC --- const handleGetInsights = async () => { if (logEntries.length < 5) { alert("Please add at least 5 readings to get meaningful insights."); return; } insightsLoader.classList.remove('hidden'); insightsOutput.classList.add('hidden'); getInsightsBtn.disabled = true; const prompt = buildPrompt(); const insightsText = await callGeminiApi(prompt); renderSummary(insightsText); insightsLoader.classList.add('hidden'); insightsOutput.classList.remove('hidden'); getInsightsBtn.disabled = false; }; const buildPrompt = () => { const recentEntries = logEntries .sort((a, b) => new Date(b.datetime) - new Date(a.datetime)) .slice(0, 20); // Analyze last 20 entries const formattedEntries = recentEntries.map(entry => { return `Date: ${formatDateTime(entry.datetime)}, Level: ${entry.value} mg/dL, Context: ${entry.context}, Notes: ${entry.notes || 'None'}`; }).join('\n'); return ` As a health data analysis AI (not a medical professional), analyze the following blood glucose log entries. **Log Data:** ${formattedEntries} **Task:** 1. **Identify Key Observations:** Look for patterns. Are fasting numbers consistently high? Are there significant spikes after certain meals (check notes)? Are there periods of stability? Mention 2-3 key observations. 2. **Highlight Potential Correlations:** Point out possible links between notes (like specific foods or activities) and glucose levels. For example, "It looks like your reading was higher after noting 'pizza dinner'." 3. **Suggest Questions for a Doctor:** Based on the data, formulate 2-3 thoughtful questions the user could ask their healthcare provider. For example, "Given my fasting glucose levels, what should be my target range?" or "I noticed a spike after eating X, is this a typical response?" **Formatting:** - Use '##' for the main title: "Summary & AI-Powered Analysis". - Use '###' for section headings: "Key Observations", "Potential Correlations", "Questions for Your Doctor". - Use bullet points (-) for lists. - Conclude with a clear medical disclaimer. `; }; 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 insights. Please try again later.\n\n**Disclaimer:** This is not medical advice."; } }; const renderSummary = (insightsText) => { document.getElementById('summary-date').textContent = `Report generated on ${new Date().toLocaleDateString('en-US')}`; const historyHtml = `

Full Log History

${logEntries.sort((a,b) => new Date(b.datetime) - new Date(a.datetime)).map(e => ` `).join('')}
Date & Time Level (mg/dL) Context Notes
${formatDateTime(e.datetime)} ${e.value} ${e.context} ${e.notes || ''}
`; const insightsHtml = insightsText .replace(/## (.*?)\n/g, '

$1

') .replace(/### (.*?)\n/g, '

$1

') .replace(/- (.*?)\n/g, '
  • $1
') .replace(/<\/ul>\s*
    /g, ''); summaryContentContainer.innerHTML = `
    ${insightsHtml}

    ${historyHtml}`; }; // --- 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 pdfHeight = pdf.internal.pageSize.getHeight(); 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 -= (pdfHeight - 20); while (heightLeft > 0) { position = -heightLeft - 10; pdf.addPage(); pdf.addImage(imgData, 'PNG', 10, position, imgWidth, imgHeight); heightLeft -= (pdfHeight - 20); } pdf.save('My-Blood-Glucose-Report.pdf'); }) .catch(err => console.error("PDF Generation Error:", err)); }; // --- UTILITIES --- const formatDateTime = (datetimeString) => { const date = new Date(datetimeString); return date.toLocaleString('en-US', { month: 'short', day: 'numeric', year: 'numeric', hour: 'numeric', minute: '2-digit', hour12: true }); }; // --- START THE APP --- initialize(); });
Scroll to Top