Spaces:
Running
Running
| class PulseApp { | |
| constructor() { | |
| this.userData = this.loadUserData(); | |
| this.init(); | |
| } | |
| init() { | |
| this.setupThemeToggle(); | |
| this.setupQuickActions(); | |
| this.startRealTimeUpdates(); | |
| this.setupVitalRings(); | |
| this.setupAICoach(); | |
| } | |
| // Theme Management | |
| setupThemeToggle() { | |
| const themeToggle = document.querySelector('#theme-toggle'); | |
| if (themeToggle) { | |
| themeToggle.addEventListener('click', () => { | |
| const html = document.documentElement; | |
| const isDark = html.classList.contains('dark'); | |
| html.classList.toggle('dark'); | |
| localStorage.setItem('theme', isDark ? 'light' : 'dark'); | |
| }); | |
| } | |
| // Load saved theme | |
| const savedTheme = localStorage.getItem('theme'); | |
| if (savedTheme === 'dark') { | |
| document.documentElement.classList.add('dark'); | |
| } | |
| } | |
| // Quick Actions Handler | |
| setupQuickActions() { | |
| const quickActionButtons = document.querySelectorAll('quick-action'); | |
| quickActionButtons.forEach(button => { | |
| button.addEventListener('click', (e) => { | |
| const action = e.currentTarget.getAttribute('data-action'); | |
| this.handleQuickAction(action); | |
| }); | |
| }); | |
| } | |
| handleQuickAction(action) { | |
| const actions = { | |
| 'food': () => this.showNotification('กำลังเปิดบันทึกอาหาร...', 'orange'), | |
| 'exercise': () => this.showNotification('กำลังเริ่มจับเวลาออกกำลังกาย...', 'red'), | |
| 'mood': () => this.showNotification('กำลังเปิดบันทึกอารมณ์...', 'pink'), | |
| 'fasting': () => this.startFastingTimer(), | |
| }; | |
| if (actions[action]) { | |
| actions[action](); | |
| } | |
| } | |
| // Fasting Timer | |
| startFastingTimer() { | |
| const startTime = Date.now(); | |
| const timerInterval = setInterval(() => { | |
| const elapsed = Date.now() - startTime; | |
| const hours = Math.floor(elapsed / 3600000); | |
| const minutes = Math.floor((elapsed % 3600000) / 60000); | |
| const timerElement = document.querySelector('#fasting-timer'); | |
| if (timerElement) { | |
| timerElement.textContent = `${hours}ชม. ${minutes}น.`; | |
| } | |
| }, 60000); | |
| this.showNotification('จับเวลาการ IF เริ่มแล้ว!', 'teal'); | |
| } | |
| // Real-time Data Updates | |
| startRealTimeUpdates() { | |
| // Simulate real-time vital changes | |
| setInterval(() => { | |
| this.updateVitalRings(); | |
| this.updateContextualData(); | |
| }, 30000); // Update every 30 seconds | |
| } | |
| updateVitalRings() { | |
| const rings = [ | |
| { selector: '.text-emerald-500', key: 'recovery', variance: 2 }, | |
| { selector: '.text-orange-500', key: 'strain', variance: 0.5 }, | |
| { selector: '.text-purple-500', key: 'sleep', variance: 3 } | |
| ]; | |
| rings.forEach(ring => { | |
| const element = document.querySelector(ring.selector); | |
| if (element) { | |
| const currentValue = parseFloat(element.textContent); | |
| const change = (Math.random() - 0.5) * ring.variance; | |
| const newValue = Math.max(0, Math.min(100, currentValue + change)); | |
| element.textContent = ring.key === 'strain' ? newValue.toFixed(1) : Math.round(newValue) + '%'; | |
| } | |
| }); | |
| } | |
| updateContextualData() { | |
| // Simulate AQI changes | |
| const aqiElement = document.querySelector('contextual-vital[title="คุณภาพอากาศ"]'); | |
| if (aqiElement) { | |
| const currentAQI = parseInt(aqiElement.getAttribute('value').match(/\d+/)[0]); | |
| const newAQI = Math.max(30, Math.min(200, currentAQI + (Math.random() - 0.5) * 10)); | |
| const status = newAQI < 50 ? 'safe' : newAQI < 100 ? 'warning' : 'danger'; | |
| const statusText = status === 'safe' ? 'ดี' : status === 'warning' ? 'ปานกลาง' : 'ไม่ดี'; | |
| aqiElement.setAttribute('value', `${statusText} (${Math.round(newAQI)})`); | |
| aqiElement.setAttribute('status', status); | |
| } | |
| } | |
| // Vital Rings Animation | |
| setupVitalRings() { | |
| const rings = document.querySelectorAll('.relative.w-24.h-24'); | |
| rings.forEach((ring, index) => { | |
| setTimeout(() => { | |
| ring.classList.add('vital-pulse'); | |
| }, index * 200); | |
| }); | |
| } | |
| // AI Coach Interactions | |
| setupAICoach() { | |
| const aiButton = document.querySelector('.bg-gradient-to-r button'); | |
| if (aiButton) { | |
| aiButton.addEventListener('click', () => { | |
| this.showDetailedRecommendations(); | |
| }); | |
| } | |
| } | |
| showDetailedRecommendations() { | |
| const modal = document.createElement('div'); | |
| modal.className = 'fixed inset-0 bg-black/50 backdrop-blur-sm z-50 flex items-center justify-center p-4'; | |
| modal.innerHTML = ` | |
| <div class="bg-white dark:bg-slate-800 rounded-2xl p-6 max-w-md w-full shadow-2xl"> | |
| <h3 class="text-lg font-semibold mb-4 text-slate-800 dark:text-white">คำแนะนำเพิ่มเติม</h3> | |
| <div class="space-y-3 mb-4"> | |
| <div class="flex items-start space-x-3"> | |
| <i data-feather="shield" class="w-5 h-5 text-green-500 mt-1"></i> | |
| <div> | |
| <p class="text-sm font-medium text-slate-800 dark:text-white">ใส่หน้ากาก N95</p> | |
| <p class="text-xs text-slate-500 dark:text-slate-400">เมื่อออกนอกบ้านในวันฝุ่นสูง</p> | |
| </div> | |
| </div> | |
| <div class="flex items-start space-x-3"> | |
| <i data-feather="droplet" class="w-5 h-5 text-blue-500 mt-1"></i> | |
| <div> | |
| <p class="text-sm font-medium text-slate-800 dark:text-white">ดื่มน้ำมากขึ้น 20%</p> | |
| <p class="text-xs text-slate-500 dark:text-slate-400">ช่วยขับสารพิษจากฝุ่น</p> | |
| </div> | |
| </div> | |
| <div class="flex items-start space-x-3"> | |
| <i data-feather="home" class="w-5 h-5 text-purple-500 mt-1"></i> | |
| <div> | |
| <p class="text-sm font-medium text-slate-800 dark:text-white">ใช้เครื่องฟอกอากาศ</p> | |
| <p class="text-xs text-slate-500 dark:text-slate-400">อย่างน้อย 2 ชม. ก่อนนอน</p> | |
| </div> | |
| </div> | |
| </div> | |
| <button onclick="this.closest('.fixed').remove()" class="w-full bg-violet-600 text-white py-2 rounded-lg hover:bg-violet-700 transition"> | |
| ปิด | |
| </button> | |
| </div> | |
| `; | |
| document.body.appendChild(modal); | |
| feather.replace(); | |
| } | |
| // Notifications | |
| showNotification(message, color = 'violet') { | |
| const notification = document.createElement('div'); | |
| notification.className = `fixed top-20 right-4 z-50 transform translate-x-full transition-transform duration-300`; | |
| notification.innerHTML = ` | |
| <div class="bg-${color}-500 text-white px-4 py-3 rounded-lg shadow-lg flex items-center space-x-2"> | |
| <i data-feather="check" class="w-4 h-4"></i> | |
| <span class="text-sm">${message}</span> | |
| </div> | |
| `; | |
| document.body.appendChild(notification); | |
| setTimeout(() => { | |
| notification.classList.remove('translate-x-full'); | |
| }, 100); | |
| setTimeout(() => { | |
| notification.classList.add('translate-x-full'); | |
| setTimeout(() => notification.remove(), 300); | |
| }, 3000); | |
| feather.replace(); | |
| } | |
| // Data Persistence | |
| loadUserData() { | |
| const defaultData = { | |
| recovery: 72, | |
| strain: 14.2, | |
| sleep: 85, | |
| theme: 'light' | |
| }; | |
| const saved = localStorage.getItem('pulseData'); | |
| return saved ? JSON.parse(saved) : defaultData; | |
| } | |
| saveUserData() { | |
| localStorage.setItem('pulseData', JSON.stringify(this.userData)); | |
| } | |
| } | |
| // Initialize App | |
| document.addEventListener('DOMContentLoaded', () => { | |
| new PulseApp(); | |
| }); | |
| // Global Utilities | |
| window.showNotification = function(message, color) { | |
| const notification = document.createElement('div'); | |
| notification.className = `fixed top-20 right-4 z-50 transform translate-x-full transition-transform duration-300`; | |
| notification.innerHTML = ` | |
| <div class="bg-${color}-500 text-white px-4 py-3 rounded-lg shadow-lg flex items-center space-x-2"> | |
| <i data-feather="check" class="w-4 h-4"></i> | |
| <span class="text-sm">${message}</span> | |
| </div> | |
| `; | |
| document.body.appendChild(notification); | |
| setTimeout(() => notification.classList.remove('translate-x-full'), 100); | |
| setTimeout(() => { | |
| notification.classList.add('translate-x-full'); | |
| setTimeout(() => notification.remove(), 300); | |
| }, 3000); | |
| feather.replace(); | |
| }; | |