import React, { useState, useEffect } from 'react'; import './App.css'; import HomeView from './views/HomeView'; import SearchView from './views/SearchView'; import ScannerView from './views/ScannerView'; import AlertsView from './views/AlertsView'; import ProfileView from './views/ProfileView'; import AdminView from './views/AdminView'; import LoginModal from './components/LoginModal'; import SavedNotifications from './components/SavedNotifications'; import BottomNav from './components/BottomNav'; import { getFaro } from './utils/faro'; function App() { const [screen, setScreen] = useState('home'); const [currentUser, setCurrentUser] = useState(null); const [authChecked, setAuthChecked] = useState(false); const [showLogin, setShowLogin] = useState(false); const [showSaved, setShowSaved] = useState(false); const [badgeCount, setBadgeCount] = useState(0); const [prescriptionSearch, setPrescriptionSearch] = useState(''); const [screenSize, setScreenSize] = useState({ width: window.innerWidth, height: window.innerHeight }); // Theme: 'auto' | 'light' | 'dark' const [theme, setThemeState] = useState(() => { return localStorage.getItem('ff-theme') || 'auto'; }); // Device detection const isMobile = screenSize.width <= 768; const isTablet = screenSize.width > 768 && screenSize.width <= 1024; const isDesktop = screenSize.width > 1024; // Apply theme to document useEffect(() => { const mq = window.matchMedia('(prefers-color-scheme: dark)'); function applyTheme() { const isDark = theme === 'dark' || (theme === 'auto' && mq.matches); document.documentElement.setAttribute('data-theme', isDark ? 'dark' : 'light'); } applyTheme(); if (theme === 'auto') { mq.addEventListener('change', applyTheme); return () => mq.removeEventListener('change', applyTheme); } }, [theme]); function setTheme(newTheme) { setThemeState(newTheme); localStorage.setItem('ff-theme', newTheme); } useEffect(() => { // Set initial screen size const handleResize = () => { setScreenSize({ width: window.innerWidth, height: window.innerHeight }); }; fetch('/api/auth/check') .then(r => r.json()) .then(data => { if (data.authenticated) setCurrentUser(data.user); }) .catch((err) => { console.warn('[auth/check]', err); getFaro()?.pushError(err, { type: 'network', url: '/api/auth/check' }); }) .finally(() => setAuthChecked(true)); window.addEventListener('resize', handleResize); return () => window.removeEventListener('resize', handleResize); }, []); useEffect(() => { if (!currentUser) { setBadgeCount(0); return; } let cancelled = false; fetch('/api/notifications/mine', { credentials: 'include' }) .then(r => r.ok ? r.json() : null) .then(data => { if (cancelled || !data) return; const count = (data.global?.length || 0) + (data.pharmacy?.length || 0); setBadgeCount(count); }) .catch((err) => { console.warn('[notifications/mine]', err); getFaro()?.pushError(err, { type: 'network', url: '/api/notifications/mine' }); }); return () => { cancelled = true; }; }, [currentUser]); function refreshBadgeCount() { if (!currentUser) return; fetch('/api/notifications/mine', { credentials: 'include' }) .then(r => r.ok ? r.json() : null) .then(data => { if (!data) return; setBadgeCount((data.global?.length || 0) + (data.pharmacy?.length || 0)); }) .catch((err) => { console.warn('[refreshBadge]', err); getFaro()?.pushError(err, { type: 'network', url: '/api/notifications/mine' }); }); } function handleLogin(user) { setCurrentUser(user); setShowLogin(false); } async function handleLogout() { await fetch('/api/auth/logout', { method: 'POST' }).catch(() => {}); setCurrentUser(null); setScreen('home'); } function handleProfileSaved(updated) { setCurrentUser(prev => ({ ...prev, ...updated })); } function handleAdminClick() { setScreen('admin'); } const isLoggedIn = Boolean(currentUser); function handleNavChange(tab) { if (tab === 'home') { setScreen('home'); return; } if (tab === 'search') { setScreen('search'); return; } if (tab === 'scan') { setScreen('scan'); return; } if (tab === 'alerts') { if (currentUser) setScreen('alerts'); else setShowLogin(true); return; } if (tab === 'profile') { if (currentUser) setScreen('profile'); else setShowLogin(true); return; } } let activeView; let deviceClass = isMobile ? 'mobile-view' : 'desktop-view'; switch (screen) { case 'profile': activeView = ( setShowSaved(true)} onLogout={handleLogout} onAdminClick={handleAdminClick} theme={theme} onThemeChange={setTheme} /> ); break; case 'alerts': activeView = ( { setPrescriptionSearch(name); setScreen('search'); }} /> ); break; case 'search': activeView = ( setShowLogin(true)} initialQuery={prescriptionSearch} /> ); break; case 'scan': activeView = ( setScreen('home')} onSelectMedicine={(name) => { setPrescriptionSearch(name); setScreen('search'); }} /> ); break; case 'admin': activeView = ; break; default: activeView = ( setShowLogin(true)} onScanClick={() => setScreen('scan')} onSearchClick={() => setScreen('search')} /> ); break; } return (
{activeView}
{showLogin && ( setShowLogin(false)} /> )} {showSaved && currentUser && ( setShowSaved(false)} onNotificationChange={refreshBadgeCount} /> )}
); } export default App;