Recovery Network — Your Day Good morning, Alex
Thursday, December 11
Synced 12m ago
Go get 'em today.
Your sleep was restorative and HRV is strong. Great day to tackle something challenging.
0 hrs
within 7.0 - 8.0 hrs
7d agoToday
0
ms
within 45 - 60 ms
7d agoToday
0
low
stable, calm state
7d agoToday
Quick Actions
Your HRV is 12% above your 7-day average. This indicates strong recovery and resilience today.
Suggestion: Good day to tackle something challenging or have a difficult conversation.
What helped yesterday: You went to bed before 10pm and had no screen time after 9pm.
Any triggers today?
28
Days Strong
"One day at a time"
My Goals
Attend 3 meetings this week 2 of 3
What's Working
Your sleep routine is consistent — bedtime before 10pm helps your HRV
Morning check-ins correlate with better mood scores
Meeting attendance on Mon/Wed/Fri shows pattern of stability
Green = checked in · Gray = missed
Before your devices can share data:
Step 1: Share my health data
Allow my devices to send data to Recovery Network
Step 2: Get AI insights
Let Dr. Olivia analyze my data to help my recovery
Complete both steps to connect your devices
Ready to connect your devices
${w.name}
${w.desc}${isCamera ? ' (wellness only)' : ''}
${isActive ? '● Active' : 'Off'}
`; }).join(''); } // === DEVICE BINDING (Only works when both consents granted) === async function executeBinding(sensorID, friendlyName, shouldActivate) { // GATE: Check both consents if (!dataConsentGranted || !aiConsentGranted) { showToast('Complete both consent steps first'); document.getElementById(`toggle-${sensorID}`).checked = false; return; } const correlationId = generateId(); const batch = db.batch(); try { const patientRef = db.collection('patients').doc(PATIENT_ID); if (shouldActivate) { batch.set(patientRef, { sensors: { [sensorID]: { ingest_consent: true, active: true, consent_rev: '1.7', consent_granted_at: new Date().toISOString(), last_sync: firebase.firestore.FieldValue.serverTimestamp() } } }, { merge: true }); } else { batch.set(patientRef, { sensors: { [sensorID]: { ingest_consent: false, active: false, deactivated_at: firebase.firestore.FieldValue.serverTimestamp() } } }, { merge: true }); } // 2. The Audit (Dr. Chen's Evidence) const auditRef = db.collection('audit').doc(); batch.set(auditRef, { action: shouldActivate ? 'LEGAL_BINDING_ESTABLISHED' : 'LEGAL_BINDING_REVOKED', actor: 'PATIENT', patient_id: PATIENT_ID, sensor: sensorID, sensor_name: friendlyName, correlation_id: correlationId, timestamp: firebase.firestore.FieldValue.serverTimestamp(), rev: '33' }); // 3. Commit the atomic transaction await batch.commit(); // 4. Dispatch signal to Phoenix Spine await dispatchPatientSignal('sensor_binding', shouldActivate ? 1 : 0, 1.0, { action: shouldActivate ? 'LEGAL_BINDING_ESTABLISHED' : 'LEGAL_BINDING_REVOKED', actor: 'PATIENT', sensor: sensorID, sensor_name: friendlyName, consent_rev: '1.7', unit: 'binding' }); // 5. UI Feedback const statusEl = document.getElementById(`status-${sensorID}`); if (statusEl) { statusEl.innerHTML = shouldActivate ? '
● Active' : '
Off'; } sensorStates[sensorID] = { ingest_consent: shouldActivate }; console.log(`[RNI SPINE] ${friendlyName} ${shouldActivate ? 'connected' : 'disconnected'}`); showToast(`${friendlyName} ${shouldActivate ? 'connected' : 'disconnected'}`); } catch (err) { console.error("Binding failed:", err); showToast('Connection failed - please try again'); const toggleEl = document.getElementById(`toggle-${sensorID}`); if (toggleEl) toggleEl.checked = !shouldActivate; } } // === MOOD CHECK-IN === let selectedMood = null; let selectedTriggers = []; async function submitMood(level) { selectedMood = level; // Update UI document.querySelectorAll('.mood-btn').forEach(btn => { btn.classList.remove('selected'); if (parseInt(btn.dataset.mood) === level) { btn.classList.add('selected'); } }); // Write to Firestore const correlationId = generateId(); try { await db.collection('patients').doc(PATIENT_ID).collection('mood_checkins').add({ mood_level: level, triggers: selectedTriggers, timestamp: firebase.firestore.FieldValue.serverTimestamp(), correlation_id: correlationId }); // Dispatch signal to Phoenix Spine await dispatchPatientSignal('mood_checkin', level, 1.0, { action: 'MOOD_REPORT', actor: 'PATIENT', triggers: selectedTriggers, unit: 'score' }); showToast('Mood recorded ✓'); console.log(`[RNI SPINE] Mood check-in: ${level}`); } catch (e) { console.error('Mood check-in failed:', e); } } function toggleTrigger(el, trigger) { el.classList.toggle('selected'); if (selectedTriggers.includes(trigger)) { selectedTriggers = selectedTriggers.filter(t => t !== trigger); } else { // If selecting "none", clear others if (trigger === 'none') { selectedTriggers = ['none']; document.querySelectorAll('.trigger-chip').forEach(chip => { chip.classList.remove('selected'); }); el.classList.add('selected'); } else { // Remove "none" if selecting another trigger selectedTriggers = selectedTriggers.filter(t => t !== 'none'); document.querySelector('.trigger-chip[onclick*="none"]')?.classList.remove('selected'); selectedTriggers.push(trigger); } } } // === JOURNAL === async function saveJournal() { const input = document.getElementById('journalInput'); const content = input.value.trim(); if (!content) { showToast('Write something first'); return; } const btn = document.querySelector('.journal-save'); btn.disabled = true; btn.innerHTML = '
Saving...'; const correlationId = generateId(); try { await db.collection('patients').doc(PATIENT_ID).collection('journal').add({ content: content, word_count: content.split(/\s+/).length, timestamp: firebase.firestore.FieldValue.serverTimestamp(), correlation_id: correlationId }); // Dispatch signal await dispatchPatientSignal('journal_entry', 1, 1.0, { action: 'JOURNAL_SUBMIT', actor: 'PATIENT', word_count: content.split(/\s+/).length, unit: 'entry' }); input.value = ''; btn.innerHTML = '
Saved!'; showToast('Journal entry saved ✓'); setTimeout(() => { btn.disabled = false; btn.innerHTML = '
Save Entry'; }, 2000); console.log(`[RNI SPINE] Journal entry saved`); } catch (e) { console.error('Journal save failed:', e); btn.disabled = false; btn.innerHTML = '
Save Entry'; showToast('Failed to save'); } } // === CHAT === async function sendChat() { const input = document.querySelector('.chat-box input'); const msg = input.value.trim(); if (!msg) return; const correlationId = generateId(); // Write to messages collection await db.collection('patients').doc(PATIENT_ID).collection('messages').add({ content: msg, direction: 'outbound', timestamp: firebase.firestore.FieldValue.serverTimestamp(), correlation_id: correlationId }); // Dispatch self_report signal to Phoenix Spine await dispatchPatientSignal('self_report', 1, 0.8, { report_type: 'chat_message', word_count: msg.split(/\s+/).length, correlation_id: correlationId }); input.value = ''; showToast('Message sent to Dr. Olivia'); } function quickChat(msg) { document.querySelector('.chat-box input').value = msg; sendChat(); } // === CHECK IN === async function doCheckIn() { const btn = document.getElementById('checkInBtn'); btn.classList.remove('pulse'); btn.innerHTML = '
...'; const correlationId = generateId(); try { // Write to check_ins collection await db.collection('patients').doc(PATIENT_ID).collection('check_ins').add({ timestamp: firebase.firestore.FieldValue.serverTimestamp(), type: 'daily', correlation_id: correlationId }); // Dispatch check_in signal to Phoenix Spine (Executive/Clinical can see this) await dispatchPatientSignal('check_in', 1, 1.0, { check_in_type: 'daily', correlation_id: correlationId }); btn.classList.add('completed'); btn.innerHTML = `
Checked InToday at ${new Date().toLocaleTimeString('en-US', {hour: 'numeric', minute: '2-digit'})}`; showToast('Check-in recorded!'); } catch (e) { console.error('Check-in failed:', e); btn.innerHTML = '
Check In'; btn.classList.add('pulse'); } } // === NAV CLICK HANDLER (Plain English for Patients) === document.querySelectorAll('.nav-item').forEach(item => { item.addEventListener('click', function() { const page = this.dataset.page; // Update active state document.querySelectorAll('.nav-item').forEach(i => i.classList.remove('active')); this.classList.add('active'); // Hide all page sections first document.getElementById('today-page').style.display = 'block'; document.getElementById('progress-page').style.display = 'none'; // Handle navigation if (page === 'today') { document.getElementById('today-page').style.display = 'block'; document.getElementById('progress-page').style.display = 'none'; } else if (page === 'devices') { // My Devices - opens consent + device modal openBindingModal(); } else if (page === 'progress') { // My Progress - show progress page document.getElementById('today-page').style.display = 'none'; document.getElementById('progress-page').style.display = 'block'; } else if (page === 'olivia') { // Talk to Dr. Olivia - focus chat document.querySelector('.chat-box input').focus(); showToast('Chat with Dr. Olivia'); } }); }); // === ACTION BUTTONS === document.querySelectorAll('.action-btn').forEach(btn => { if (btn.id === 'checkInBtn') { btn.addEventListener('click', doCheckIn); } else if (btn.id === 'findMeeting') { btn.addEventListener('click', () => { window.open('https://www.aa.org/find-aa', '_blank'); }); } else if (btn.querySelector('span')?.textContent === 'Call Sponsor') { btn.addEventListener('click', () => { showToast('Opening phone...'); // In production: window.location.href = 'tel:+15551234567'; }); } else if (btn.querySelector('span')?.textContent === 'Daily Reading') { btn.addEventListener('click', () => { window.open('https://www.aa.org/daily-reflection', '_blank'); }); } }); // === CHAT WIRING === document.querySelector('.chat-send').addEventListener('click', sendChat); document.querySelector('.chat-box input').addEventListener('keypress', (e) => { if (e.key === 'Enter') sendChat(); }); document.querySelectorAll('.chip').forEach(chip => { chip.addEventListener('click', () => quickChat(chip.textContent)); }); // === ANIMATIONS (from Rev 24) === document.addEventListener('DOMContentLoaded', () => { // Gauge animation const gauge = document.querySelector('.gauge-bar'); const gaugeNum = document.querySelector('.gauge-num'); gauge.style.strokeDashoffset = '150'; setTimeout(() => { gauge.style.strokeDashoffset = '20'; }, 300); let current = 0; const target = 87; const counter = setInterval(() => { current++; gaugeNum.textContent = current; if (current >= target) clearInterval(counter); }, 14); }); // Day counter animation document.addEventListener('DOMContentLoaded', () => { const dayNum = document.querySelector('.day-num'); if (!dayNum) return; let current = 0; const target = 28; setTimeout(() => { const counter = setInterval(() => { current++; dayNum.textContent = current; if (current >= target) clearInterval(counter); }, 43); }, 500); }); // Metric animations document.addEventListener('DOMContentLoaded', () => { const metrics = [ { selector: '.metric:nth-child(1) .metric-num', target: 7.2, decimal: true, delay: 300 }, { selector: '.metric:nth-child(2) .metric-num', target: 52, decimal: false, delay: 500 }, { selector: '.metric:nth-child(3) .metric-num', target: 1.4, decimal: true, delay: 700 } ]; metrics.forEach((m) => { const el = document.querySelector(m.selector); if (!el) return; let current = 0; const steps = 25; const increment = m.target / steps; setTimeout(() => { const counter = setInterval(() => { current += increment; if (current >= m.target) { current = m.target; clearInterval(counter); } el.textContent = m.decimal ? current.toFixed(1) : Math.round(current); }, 32); }, m.delay); }); }); // Close modal on backdrop click document.getElementById('binding-modal').addEventListener('click', (e) => { if (e.target.id === 'binding-modal') closeBindingModal(); }); console.log('========================================'); console.log('🩺 RNI Patient Portal Rev 33'); console.log('✓ Plain English menu (Today, My Devices, My Progress, Talk to Dr. Olivia)'); console.log('✓ Two Green Dots consent flow'); console.log('✓ My Progress page with goals'); console.log('✓ Milestone markers (7, 14, 30, 90, 1Y)'); console.log('✓ Check-in streak calendar'); console.log('✓ Phoenix Spine connected'); console.log('========================================');