import React, { useState, useEffect } from 'react'; import { Heart, Utensils, Brain, Pill, Calendar, Plus, Activity, Download, Mail, Edit2, Trash2 } from 'lucide-react'; import { LineChart, Line, XAxis, YAxis, CartesianGrid, Tooltip, ResponsiveContainer, Legend } from 'recharts'; const ElderCareTracker = () => { const [activeProfile, setActiveProfile] = useState(null); const [profiles, setProfiles] = useState([]); const [activeTab, setActiveTab] = useState('dashboard'); const [showAddForm, setShowAddForm] = useState(null); const [showExportMenu, setShowExportMenu] = useState(false); const [editingEntry, setEditingEntry] = useState(null); const [entries, setEntries] = useState({ vitals: [], meals: [], mental: [], medications: [], notes: [] }); useEffect(() => { loadData(); }, []); useEffect(() => { if (activeProfile) { saveData(); } }, [entries, profiles, activeProfile]); const loadData = async () => { try { const profilesResult = await window.storage.get('elder-care-profiles'); if (profilesResult) { const loadedProfiles = JSON.parse(profilesResult.value); setProfiles(loadedProfiles); if (loadedProfiles.length > 0) { setActiveProfile(loadedProfiles[0].id); await loadProfileData(loadedProfiles[0].id); } } } catch (error) { console.log('No existing profiles found'); } }; const loadProfileData = async (profileId) => { try { const result = await window.storage.get(`elder-care-data-${profileId}`); if (result) { setEntries(JSON.parse(result.value)); } else { setEntries({ vitals: [], meals: [], mental: [], medications: [], notes: [] }); } } catch (error) { setEntries({ vitals: [], meals: [], mental: [], medications: [], notes: [] }); } }; const saveData = async () => { if (!activeProfile) return; try { await window.storage.set('elder-care-profiles', JSON.stringify(profiles)); await window.storage.set(`elder-care-data-${activeProfile}`, JSON.stringify(entries)); } catch (error) { console.error('Error saving:', error); } }; const addProfile = (name) => { const newProfile = { id: Date.now().toString(), name, createdAt: new Date().toISOString() }; setProfiles([...profiles, newProfile]); setActiveProfile(newProfile.id); setEntries({ vitals: [], meals: [], mental: [], medications: [], notes: [] }); }; const addVital = (data) => { const timestamp = data.date ? new Date(data.date + 'T' + new Date().toTimeString().split(' ')[0]).toISOString() : new Date().toISOString(); const newVital = { id: Date.now().toString(), timestamp, ...data }; delete newVital.date; setEntries(prev => ({ ...prev, vitals: [newVital, ...prev.vitals] })); setShowAddForm(null); }; const addMeal = (data) => { setEntries(prev => ({ ...prev, meals: [{ id: Date.now().toString(), timestamp: new Date().toISOString(), ...data }, ...prev.meals] })); setShowAddForm(null); }; const addMentalState = (data) => { const timestamp = data.date ? new Date(data.date + 'T' + new Date().toTimeString().split(' ')[0]).toISOString() : new Date().toISOString(); const newMental = { id: Date.now().toString(), timestamp, ...data }; delete newMental.date; setEntries(prev => ({ ...prev, mental: [newMental, ...prev.mental] })); setShowAddForm(null); }; const addMedication = (data) => { const timestamp = data.date ? new Date(data.date + 'T' + new Date().toTimeString().split(' ')[0]).toISOString() : new Date().toISOString(); const newMed = { id: Date.now().toString(), timestamp, ...data }; delete newMed.date; setEntries(prev => ({ ...prev, medications: [newMed, ...prev.medications] })); setShowAddForm(null); }; const addNote = (data) => { setEntries(prev => ({ ...prev, notes: [{ id: Date.now().toString(), timestamp: new Date().toISOString(), ...data }, ...prev.notes] })); setShowAddForm(null); }; const updateEntry = (type, updatedData) => { setEntries(prev => ({ ...prev, [type]: prev[type].map(entry => entry.id === updatedData.id ? updatedData : entry) })); setEditingEntry(null); setShowAddForm(null); }; const deleteEntry = (type, id) => { if (confirm('Delete this entry?')) { setEntries(prev => ({ ...prev, [type]: prev[type].filter(entry => entry.id !== id) })); } }; const formatDate = (isoString) => { const date = new Date(isoString); return date.toLocaleDateString() + ' ' + date.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' }); }; const formatChartDate = (isoString) => { const date = new Date(isoString); return date.toLocaleDateString([], { month: 'short', day: 'numeric' }); }; const getActiveProfileName = () => { const profile = profiles.find(p => p.id === activeProfile); return profile ? profile.name : ''; }; const exportData = () => { const dataStr = JSON.stringify({ profile: getActiveProfileName(), exportDate: new Date().toISOString(), data: entries }, null, 2); const blob = new Blob([dataStr], { type: 'application/json' }); const url = URL.createObjectURL(blob); const link = document.createElement('a'); link.href = url; link.download = `elder-care-${getActiveProfileName().replace(/\s+/g, '-')}-${new Date().toISOString().split('T')[0]}.json`; document.body.appendChild(link); link.click(); document.body.removeChild(link); URL.revokeObjectURL(url); setShowExportMenu(false); }; const exportCSV = () => { const profileName = getActiveProfileName(); let csv = `Elder Care Export - ${profileName}\n\n`; if (entries.vitals.length > 0) { csv += 'VITALS\nDate,Time,BP,Blood Sugar,HR,Temp,O2,Notes\n'; entries.vitals.forEach(v => { const dt = new Date(v.timestamp); const bp = (v.systolic && v.diastolic) ? `${v.systolic}/${v.diastolic}` : ''; csv += `${dt.toLocaleDateString()},${dt.toLocaleTimeString()},${bp},${v.bloodSugar||''},${v.heartRate||''},${v.temperature||''},${v.oxygen||''},"${(v.notes||'').replace(/"/g,'""')}"\n`; }); csv += '\n'; } if (entries.mental.length > 0) { csv += 'MENTAL STATE\nDate,Time,Mood,Outburst,Subjects,Duration,Intensity,Notes\n'; entries.mental.forEach(m => { const dt = new Date(m.timestamp); const subj = (m.subjects && m.subjects.length) ? m.subjects.join('; ') : ''; csv += `${dt.toLocaleDateString()},${dt.toLocaleTimeString()},${m.mood}/10,${m.hasOutburst?'Yes':'No'},"${subj}",${m.duration||''},${m.intensity||''},"${(m.notes||'').replace(/"/g,'""')}"\n`; }); csv += '\n'; } if (entries.medications.length > 0) { csv += 'MEDS\nDate,Time,Morning,Morning Status,Evening,Evening Status,Notes\n'; entries.medications.forEach(m => { const dt = new Date(m.timestamp); csv += `${dt.toLocaleDateString()},${dt.toLocaleTimeString()},${m.morningMeds?'Yes':'No'},${m.morningMeds?m.morningTakenAs:''},${m.eveningMeds?'Yes':'No'},${m.eveningMeds?m.eveningTakenAs:''},"${(m.notes||'').replace(/"/g,'""')}"\n`; }); csv += '\n'; } const blob = new Blob([csv], { type: 'text/csv' }); const url = URL.createObjectURL(blob); const link = document.createElement('a'); link.href = url; link.download = `elder-care-${profileName.replace(/\s+/g, '-')}-${new Date().toISOString().split('T')[0]}.csv`; document.body.appendChild(link); link.click(); document.body.removeChild(link); URL.revokeObjectURL(url); setShowExportMenu(false); }; const emailData = () => { const name = getActiveProfileName(); const body = `Elder Care Report for ${name}\n\nVitals: ${entries.vitals.length}\nMental: ${entries.mental.length}\nMeds: ${entries.medications.length}`; window.location.href = `mailto:?subject=${encodeURIComponent('Elder Care Report - '+name)}&body=${encodeURIComponent(body)}`; setShowExportMenu(false); }; if (profiles.length === 0) { return (

Elder Care Tracker

Create a profile to start

); } return (

Elder Care Tracker

{getActiveProfileName()}

{showExportMenu && (
)}
{[ { id: 'dashboard', label: 'Dashboard', icon: Activity }, { id: 'vitals', label: 'Vitals', icon: Heart }, { id: 'meals', label: 'Meals', icon: Utensils }, { id: 'mental', label: 'Mental', icon: Brain }, { id: 'meds', label: 'Meds', icon: Pill }, { id: 'notes', label: 'Notes', icon: Calendar } ].map(tab => { const Icon = tab.icon; return ( ); })}
{activeTab === 'dashboard' && } {activeTab === 'vitals' && setShowAddForm('vitals')} onEdit={(e) => { setEditingEntry(e); setShowAddForm('vitals'); }} onDelete={(id) => deleteEntry('vitals', id)} formatDate={formatDate} />} {activeTab === 'meals' && setShowAddForm('meals')} onEdit={(e) => { setEditingEntry(e); setShowAddForm('meals'); }} onDelete={(id) => deleteEntry('meals', id)} formatDate={formatDate} />} {activeTab === 'mental' && setShowAddForm('mental')} onEdit={(e) => { setEditingEntry(e); setShowAddForm('mental'); }} onDelete={(id) => deleteEntry('mental', id)} formatDate={formatDate} />} {activeTab === 'meds' && setShowAddForm('meds')} onEdit={(e) => { setEditingEntry(e); setShowAddForm('meds'); }} onDelete={(id) => deleteEntry('medications', id)} formatDate={formatDate} />} {activeTab === 'notes' && setShowAddForm('notes')} onEdit={(e) => { setEditingEntry(e); setShowAddForm('notes'); }} onDelete={(id) => deleteEntry('notes', id)} formatDate={formatDate} />}
{showAddForm && (
{showAddForm === 'vitals' && updateEntry('vitals', d) : addVital} onCancel={() => { setShowAddForm(null); setEditingEntry(null); }} />} {showAddForm === 'meals' && updateEntry('meals', d) : addMeal} onCancel={() => { setShowAddForm(null); setEditingEntry(null); }} />} {showAddForm === 'mental' && updateEntry('mental', d) : addMentalState} onCancel={() => { setShowAddForm(null); setEditingEntry(null); }} />} {showAddForm === 'meds' && updateEntry('medications', d) : addMedication} onCancel={() => { setShowAddForm(null); setEditingEntry(null); }} />} {showAddForm === 'notes' && updateEntry('notes', d) : addNote} onCancel={() => { setShowAddForm(null); setEditingEntry(null); }} />}
)}
); }; const ProfileForm = ({ onSubmit }) => { const [name, setName] = useState(''); return (
setName(e.target.value)} placeholder="Enter name" className="w-full px-4 py-3 border rounded-lg" />
); }; const Dashboard = ({ entries, formatDate, formatChartDate }) => { const today = new Date().toISOString().split('T')[0]; const todayVitals = entries.vitals.filter(v => v.timestamp.startsWith(today)); const todayMental = entries.mental.filter(m => m.timestamp.startsWith(today)); const todayMeds = entries.medications.filter(m => m.timestamp.startsWith(today)); // Get daily data for last 14 days const getDailyData = () => { const dailyMap = new Map(); for (let i = 13; i >= 0; i--) { const date = new Date(); date.setDate(date.getDate() - i); const dateStr = date.toISOString().split('T')[0]; dailyMap.set(dateStr, { date: formatChartDate(date.toISOString()), vitalsCount: 0, bpReadings: [], bsReadings: [], mentalEntries: 0, moodAvg: null, moods: [], outburstCount: 0, outburstDetails: [], medsOnTime: 0, medsLate: 0, medsMissed: 0, medsTotal: 0 }); } entries.vitals.forEach(entry => { const dateStr = entry.timestamp.split('T')[0]; if (dailyMap.has(dateStr)) { const day = dailyMap.get(dateStr); day.vitalsCount++; if (entry.systolic && entry.diastolic) { day.bpReadings.push({ systolic: entry.systolic, diastolic: entry.diastolic }); } if (entry.bloodSugar) { day.bsReadings.push(entry.bloodSugar); } } }); entries.mental.forEach(entry => { const dateStr = entry.timestamp.split('T')[0]; if (dailyMap.has(dateStr)) { const day = dailyMap.get(dateStr); day.mentalEntries++; day.moods.push(entry.mood); if (entry.hasOutburst) { day.outburstCount++; day.outburstDetails.push({ intensity: entry.intensity, duration: entry.duration, subjects: entry.subjects || [] }); } } }); // Calculate average mood dailyMap.forEach((day) => { if (day.moods.length > 0) { day.moodAvg = Math.round((day.moods.reduce((a, b) => a + b, 0) / day.moods.length) * 10) / 10; } }); entries.medications.forEach(entry => { const dateStr = entry.timestamp.split('T')[0]; if (dailyMap.has(dateStr)) { const day = dailyMap.get(dateStr); if (entry.morningMeds) { day.medsTotal++; if (entry.morningTakenAs === 'ontime') day.medsOnTime++; else if (entry.morningTakenAs === 'late') day.medsLate++; else if (entry.morningTakenAs === 'missed') day.medsMissed++; } if (entry.eveningMeds) { day.medsTotal++; if (entry.eveningTakenAs === 'ontime') day.medsOnTime++; else if (entry.eveningTakenAs === 'late') day.medsLate++; else if (entry.eveningTakenAs === 'missed') day.medsMissed++; } } }); return Array.from(dailyMap.values()); }; const dailyData = getDailyData(); // Blood pressure data const bpData = entries.vitals .filter(v => v.systolic && v.diastolic) .slice(0, 14) .reverse() .map(v => ({ date: formatChartDate(v.timestamp), systolic: v.systolic, diastolic: v.diastolic })); // Blood sugar data const bsData = entries.vitals .filter(v => v.bloodSugar) .slice(0, 14) .reverse() .map(v => ({ date: formatChartDate(v.timestamp), bloodSugar: v.bloodSugar })); // Mood data with outbursts const moodData = entries.mental .slice(0, 14) .reverse() .map(m => ({ date: formatChartDate(m.timestamp), mood: m.mood, hasOutburst: m.hasOutburst, outburstInfo: m.hasOutburst ? `${m.intensity} - ${m.duration}${m.subjects && m.subjects.length ? '\nSubjects: ' + m.subjects.join(', ') : ''}` : null })); // Meds compliance data const medsData = dailyData.filter(d => d.medsTotal > 0); const CustomTooltip = ({ active, payload }) => { if (active && payload && payload.length) { const data = payload[0].payload; return (

{data.date}

{data.outburstInfo && (

Outburst Details:

{data.outburstInfo}

)} {data.mood !== undefined && (

Mood: {data.mood}/10

)}
); } return null; }; return (

Dashboard

{/* Today's Summary Cards */}

Today's Vitals

{todayVitals.length > 0 ? (
{todayVitals.map((v, idx) => (
{v.systolic &&

BP: {v.systolic}/{v.diastolic}

} {v.bloodSugar &&

Sugar: {v.bloodSugar} mg/dL

}

{formatDate(v.timestamp)}

))}
) : (

No vitals recorded today

)}

Today's Mental State

{todayMental.length > 0 ? (
{todayMental.map((m, idx) => (

Mood: {m.mood}/10

{m.hasOutburst && (

⚠️ Outburst: {m.intensity}

)}

{formatDate(m.timestamp)}

))}
) : (

No mental state entries today

)}

Today's Medications

{todayMeds.length > 0 ? (
{todayMeds.map((m, idx) => (
{m.morningMeds && (

Morning: {m.morningTakenAs}

)} {m.eveningMeds && (

Evening: {m.eveningTakenAs}

)}

{formatDate(m.timestamp)}

))}
) : (

No medications recorded today

)}
{/* Daily Activity Summary Chart */}

Daily Activity Summary (Last 14 Days)

{/* Blood Pressure Chart */} {bpData.length > 0 && (

Blood Pressure Trend (Last 14 Readings)

)} {/* Blood Sugar Chart */} {bsData.length > 0 && (

Blood Sugar Trend (Last 14 Readings)

)} {/* Mood & Outbursts Visual */} {moodData.length > 0 && (

Mood & Outbursts (Last 14 Entries)

{moodData.reverse().map((entry, idx) => (
{entry.date}
{/* Mood bar */}
= 7 ? 'bg-green-500' : entry.mood >= 4 ? 'bg-yellow-500' : 'bg-red-500' }`} style={{ width: `${(entry.mood / 10) * 100}%` }} >
{entry.mood}/10
{/* Outburst indicator */} {entry.hasOutburst && (
⚠️ Outburst
)}
))}
Good Mood (7-10)
Moderate (4-6)
Low Mood (0-3)
⚠️ Hover for outburst details
)} {/* Medication Compliance Visual */} {medsData.length > 0 && (

Medication Compliance (Last 14 Days)

{medsData.reverse().map((day, idx) => (
{day.date}
{Array(day.medsOnTime).fill(0).map((_, i) => (
))} {Array(day.medsLate).fill(0).map((_, i) => (
))} {Array(day.medsMissed).fill(0).map((_, i) => (
))}
{day.medsTotal} total
))}
On Time
Late
Missed
)}
); }; const VitalsTab = ({ entries, onAdd, onEdit, onDelete, formatDate }) => { return (

Vital Signs

{entries.map(e => (
Vitals
{formatDate(e.timestamp)}
{e.systolic &&

BP: {e.systolic}/{e.diastolic}

} {e.bloodSugar &&

Sugar: {e.bloodSugar}

}
))}
); }; const MealsTab = ({ entries, onAdd, onEdit, onDelete, formatDate }) => { return (

Meals

{entries.map(e => (
{e.mealType}
{formatDate(e.timestamp)}
{e.description &&

{e.description}

}
))}
); }; const MentalTab = ({ entries, onAdd, onEdit, onDelete, formatDate }) => { return (

Mental State

{entries.map(e => (
Mental State
{formatDate(e.timestamp)}

Mood: {e.mood}/10

{e.hasOutburst &&

⚠️ Outburst: {e.intensity}

}
))}
); }; const MedsTab = ({ entries, onAdd, onEdit, onDelete, formatDate }) => { return (

Medications

{entries.map(e => (
Medications
{formatDate(e.timestamp)}
{e.morningMeds &&

Morning: {e.morningTakenAs}

} {e.eveningMeds &&

Evening: {e.eveningTakenAs}

}
))}
); }; const NotesTab = ({ entries, onAdd, onEdit, onDelete, formatDate }) => { return (

Notes

{entries.map(e => (
{e.category}
{formatDate(e.timestamp)}

{e.note}

))}
); }; const VitalForm = ({ entry, onSubmit, onCancel }) => { const [data, setData] = useState({ date: entry ? new Date(entry.timestamp).toISOString().split('T')[0] : new Date().toISOString().split('T')[0], systolic: entry?.systolic || '', diastolic: entry?.diastolic || '', bloodSugar: entry?.bloodSugar || '', heartRate: entry?.heartRate || '', temperature: entry?.temperature || '', oxygen: entry?.oxygen || '', notes: entry?.notes || '' }); const handleSubmit = () => { const clean = {}; Object.keys(data).forEach(k => { if (data[k]) clean[k] = k === 'notes' || k === 'date' ? data[k] : parseFloat(data[k]) || data[k]; }); if (entry) { const ts = data.date ? new Date(data.date + 'T' + new Date(entry.timestamp).toTimeString().split(' ')[0]).toISOString() : entry.timestamp; onSubmit({ ...entry, ...clean, timestamp: ts, date: undefined }); } else { onSubmit(clean); } }; return (

{entry ? 'Edit' : 'Add'} Vitals

setData({...data, date: e.target.value})} className="w-full px-3 py-2 border rounded-lg" />
setData({...data, systolic: e.target.value})} className="px-3 py-2 border rounded-lg" /> setData({...data, diastolic: e.target.value})} className="px-3 py-2 border rounded-lg" />
setData({...data, bloodSugar: e.target.value})} className="w-full px-3 py-2 border rounded-lg" />