Personalized Dental Hygiene Routine Planner

Personalized Dental Hygiene Routine

My Dental Goals

Morning Routine

Evening Routine

${rec.reason}

Key Ingredients: ${rec.ingredients}

Examples: ${rec.examples}

`; renderedMouthwash.add(rec.reason); } }); if (renderedMouthwash.size === 0) html += '

Select a goal to see mouthwash recommendations.

'; html += '
'; recommendationsContainer.innerHTML = html; }; const saveCurrentState = () => { routineData.goals = Array.from(document.querySelectorAll('input[name="goal"]:checked')).map(cb => cb.value); const completed = Array.from(document.querySelectorAll('input[name^="task"]:checked')).map(cb => cb.id); const date = logDateInput.value; const existingLogIndex = routineData.logs.findIndex(log => log.date === date); if (existingLogIndex > -1) { routineData.logs[existingLogIndex].completed = completed; } else { routineData.logs.push({ date, completed }); } localStorage.setItem('dentalPlannerData', JSON.stringify(routineData)); }; const saveAndNotify = () => { saveCurrentState(); alert('Your log for today has been saved!'); renderDashboard(); }; const renderDashboard = () => { routineData.logs.sort((a, b) => new Date(b.date) - new Date(a.date)); renderHistoryTable(); renderConsistencyChart(); }; const renderHistoryTable = () => { if (routineData.logs.length === 0) { historyTableContainer.innerHTML = `

No logs yet. Go to the "Routine Planner" to start.

`; return; } let tableHTML = ``; routineData.logs.forEach((log, index) => { const visibleTasks = Object.keys(ROUTINE_TASKS).filter(key => routineData.goals.some(goal => ROUTINE_TASKS[key].goals.includes(goal))); const totalTasks = visibleTasks.length * 2; const completedCount = log.completed.length; const consistency = totalTasks > 0 ? Math.round((completedCount / totalTasks) * 100) : 0; tableHTML += ` `; }); tableHTML += `
Date Consistency Actions
${new Date(log.date + 'T00:00:00').toLocaleDateString()} ${consistency}%
`; historyTableContainer.innerHTML = tableHTML; }; const renderConsistencyChart = () => { if (consistencyChart) consistencyChart.destroy(); if (routineData.logs.length === 0) { chartCanvas.style.display = 'none'; return; } chartCanvas.style.display = 'block'; const chartData = [...routineData.logs].reverse().slice(-30); const labels = chartData.map(d => new Date(d.date + 'T00:00:00').toLocaleDateString()); const scores = chartData.map(log => { const visibleTasks = Object.keys(ROUTINE_TASKS).filter(key => routineData.goals.some(goal => ROUTINE_TASKS[key].goals.includes(goal))); const totalTasks = visibleTasks.length * 2; return totalTasks > 0 ? Math.round((log.completed.length / totalTasks) * 100) : 0; }); consistencyChart = new Chart(chartCanvas, { type: 'line', data: { labels: labels, datasets: [{ label: 'Consistency Score (%)', data: scores, borderColor: '#0ea5e9', backgroundColor: 'rgba(14, 165, 233, 0.1)', fill: true, tension: 0.2 }] }, options: { responsive: true, scales: { y: { beginAtZero: true, max: 100 } } } }); }; // IV. Event Listeners & Tab Logic tabs.forEach(tab => { tab.addEventListener('click', () => { tabs.forEach(t => t.classList.replace('active', 'inactive')); tab.classList.replace('inactive', 'active'); tabContents.forEach(c => c.classList.add('hidden')); document.getElementById(tab.dataset.tab).classList.remove('hidden'); if (tab.dataset.tab === 'recommender') { renderRecommendations(); } }); }); goalsContainer.addEventListener('change', () => { routineData.goals = Array.from(document.querySelectorAll('input[name="goal"]:checked')).map(cb => cb.value); updateVisibleTasks(); }); logDateInput.addEventListener('change', () => { populateRoutineChecklists(); }); saveEntryBtn.addEventListener('click', saveAndNotify); historyTableContainer.addEventListener('click', (e) => { if (e.target.classList.contains('delete-btn')) { const dateToDelete = e.target.dataset.date; if (confirm(`Are you sure you want to delete the log for ${new Date(dateToDelete + 'T00:00:00').toLocaleDateString()}?`)) { routineData.logs = routineData.logs.filter(log => log.date !== dateToDelete); localStorage.setItem('dentalPlannerData', JSON.stringify(routineData)); renderDashboard(); } } }); // V. PDF Generation pdfDownloadBtn.addEventListener('click', () => { if (routineData.logs.length === 0) return alert('No data to generate a report.'); saveCurrentState(); const { jsPDF } = jspdf; const doc = new jsPDF(); const pageW = doc.internal.pageSize.getWidth(); const margin = 15; doc.setFillColor(14, 165, 233); doc.rect(0, 0, pageW, 25, 'F'); doc.setFontSize(16); doc.setFont('helvetica', 'bold'); doc.setTextColor(255, 255, 255); doc.text('Dental Hygiene Progress Report', margin, 16); doc.setFontSize(12); doc.setFont('helvetica', 'bold'); doc.setTextColor(30, 41, 59); doc.text('Your Goals:', margin, 40); doc.setFontSize(10); doc.setFont('helvetica', 'normal'); doc.text(routineData.goals.map(g => GOALS[g].label).join(', '), margin, 48); // Add Recommendations to PDF let tableStartY = 60; doc.setFontSize(12); doc.setFont('helvetica', 'bold'); doc.text('Product Recommendations', margin, tableStartY); const recommendationData = []; const renderedRecs = new Set(); routineData.goals.forEach(goal => { const tp_rec = PRODUCT_RECOMMENDATIONS.toothpaste[goal]; if(tp_rec && !renderedRecs.has(tp_rec.reason)) { recommendationData.push(['Toothpaste', tp_rec.reason, tp_rec.ingredients]); renderedRecs.add(tp_rec.reason); } const mw_rec = PRODUCT_RECOMMENDATIONS.mouthwash[goal]; if(mw_rec && !renderedRecs.has(mw_rec.reason)) { recommendationData.push(['Mouthwash', mw_rec.reason, mw_rec.ingredients]); renderedRecs.add(mw_rec.reason); } }); doc.autoTable({ startY: tableStartY + 5, head: [['Product Type', 'Recommendation For', 'Key Ingredients']], body: recommendationData, headStyles: { fillColor: [71, 85, 105] }, theme: 'grid' }); let chartStartY = doc.lastAutoTable.finalY + 10; if (consistencyChart && chartCanvas.style.display !== 'none') { const chartImage = chartCanvas.toDataURL('image/png', 1.0); doc.setFontSize(12); doc.setFont('helvetica', 'bold'); doc.text('Consistency Chart', margin, chartStartY); doc.addImage(chartImage, 'PNG', margin, chartStartY + 5, pageW - (margin * 2), (pageW - (margin * 2)) * 0.5); } const logTableStartY = doc.lastAutoTable.finalY + 85; doc.setFontSize(12); doc.setFont('helvetica', 'bold'); doc.text('Detailed Log', margin, logTableStartY); const tableData = routineData.logs.map(log => { const visibleTasks = Object.keys(ROUTINE_TASKS).filter(key => routineData.goals.some(goal => ROUTINE_TASKS[key].goals.includes(goal))); const totalTasks = visibleTasks.length * 2; const score = totalTasks > 0 ? `${log.completed.length}/${totalTasks}` : 'N/A'; return [new Date(log.date + 'T00:00:00').toLocaleDateString(), score]; }); doc.autoTable({ startY: logTableStartY + 5, head: [['Date', 'Tasks Completed']], body: tableData, headStyles: { fillColor: [71, 85, 105] }, theme: 'grid' }); doc.save('Dental_Hygiene_Report.pdf'); }); // Initial Load logDateInput.valueAsDate = new Date(); populateGoals(); populateRoutineChecklists(); updateVisibleTasks(); renderDashboard(); });
Scroll to Top