diligence/frontend/src/App.jsx
Claude 04e2af1a85 UX overhaul: navigation, category grouping, setup guidance, checklist
Fix 1: list_providers returns category, warning, sync_status
Fix 2: Gear icon in header (offset from HelpButton) links to /settings
Fix 3: Integrations page grouped by category (Devices → AI → Nutrition →
       Notifications) with section headers, explanatory text, sync status
       badges, COROS/coming-soon handling, link to /agent, hash-scroll
Fix 4: Coach page two-path setup (built-in prominent, external agent
       subtle link to /agent), anchor to #ai-coaching
Fix 5: Settings 'all 11 providers' → 'All integrations', AI Agent link
Fix 6: AgentConnect back-link to integrations
Fix 7: Dashboard getting-started checklist (dismiss to localStorage)
2026-07-13 23:01:57 +00:00

147 lines
6.2 KiB
JavaScript
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { Routes, Route, Navigate, NavLink, useNavigate, useLocation } from 'react-router-dom'
import { useState, useEffect } from 'react'
import { hasToken, clearToken, api } from './api'
import Login from './pages/Login'
import Dashboard from './pages/Dashboard'
import LogActivity from './pages/LogActivity'
import LogFood from './pages/LogFood'
import Nutrition from './pages/Nutrition'
import Rewards from './pages/Rewards'
import Settings from './pages/Settings'
import Onboarding from './pages/Onboarding'
import WeekView from './pages/WeekView'
import Welcome from './pages/Welcome'
import SettingsIntegrations from './pages/SettingsIntegrations'
import MealPlan from './pages/MealPlan'
import ProgramSearch from './pages/ProgramSearch'
import ProgramDetail from './pages/ProgramDetail'
import CatalogDetail from './pages/CatalogDetail'
import Support from './pages/Support'
import SupportAdmin from './pages/SupportAdmin'
import ChatCoach from './pages/ChatCoach'
import AgentConnect from './pages/AgentConnect'
function ProtectedRoute({ children }) {
if (!hasToken()) return <Navigate to="/login" />
return children
}
function HelpButton() {
const [unread, setUnread] = useState(0)
const navigate = useNavigate()
const location = useLocation()
// Don't show on login/onboarding/support pages
const hidden = ['/login', '/onboarding', '/support'].some(p => location.pathname.startsWith(p))
useEffect(() => {
if (hidden || !hasToken()) return
api.getUnreadCount()
.then(d => setUnread(d.unread || 0))
.catch(() => {})
// Poll every 60s for new replies
const interval = setInterval(() => {
api.getUnreadCount()
.then(d => setUnread(d.unread || 0))
.catch(() => {})
}, 60000)
return () => clearInterval(interval)
}, [location.pathname, hidden])
if (hidden) return null
return (
<button
onClick={() => navigate('/support')}
style={{
position: 'fixed', top: '12px', right: '12px', zIndex: 90,
width: '40px', height: '40px', borderRadius: '50%',
background: 'var(--card)', boxShadow: 'var(--shadow-2)',
border: '1px solid var(--divider)',
display: 'flex', alignItems: 'center', justifyContent: 'center',
fontSize: '1.1rem', padding: 0, cursor: 'pointer',
}}
>
?
{unread > 0 && (
<span style={{
position: 'absolute', top: '-4px', right: '-4px',
background: 'var(--red)', color: '#fff', fontSize: '0.6rem', fontWeight: 800,
width: '18px', height: '18px', borderRadius: '50%',
display: 'flex', alignItems: 'center', justifyContent: 'center',
border: '2px solid var(--bg)',
}}>
{unread}
</span>
)}
</button>
)
}
function NavBar() {
return (
<nav className="nav-bar">
<NavLink to="/" className={({ isActive }) => `nav-item ${isActive ? 'active' : ''}`}>
<span className="nav-icon">🏠</span> Home
</NavLink>
<NavLink to="/log" className={({ isActive }) => `nav-item ${isActive ? 'active' : ''}`}>
<span className="nav-icon">💪</span> Log
</NavLink>
<NavLink to="/nutrition" className={({ isActive }) => `nav-item ${isActive ? 'active' : ''}`}>
<span className="nav-icon">🥑</span> Keto
</NavLink>
<NavLink to="/programs" className={({ isActive }) => `nav-item ${isActive ? 'active' : ''}`}>
<span className="nav-icon">📋</span> Programs
</NavLink>
<NavLink to="/chat" className={({ isActive }) => `nav-item ${isActive ? 'active' : ''}`}>
<span className="nav-icon">🧠</span> Coach
</NavLink>
<NavLink to="/agent" className={({ isActive }) => `nav-item ${isActive ? 'active' : ''}`}>
<span className="nav-icon">🔌</span> Agent
</NavLink>
</nav>
)
}
export default function App() {
return (
<>
{hasToken() && <HelpButton />}
{hasToken() && (
<NavLink to="/settings" style={{
position: 'fixed', top: '12px', right: '52px', zIndex: 90,
width: '36px', height: '36px', borderRadius: '50%',
background: 'var(--card)', border: '1px solid var(--card-border)',
display: 'flex', alignItems: 'center', justifyContent: 'center',
textDecoration: 'none', fontSize: '1.1rem', boxShadow: 'var(--shadow-1)',
color: 'var(--text-3)',
}}>
</NavLink>
)}
<Routes>
<Route path="/login" element={<Login />} />
<Route path="/onboarding" element={<ProtectedRoute><Onboarding /></ProtectedRoute>} />
<Route path="/" element={<ProtectedRoute><Dashboard /></ProtectedRoute>} />
<Route path="/log" element={<ProtectedRoute><LogActivity /></ProtectedRoute>} />
<Route path="/food" element={<ProtectedRoute><LogFood /></ProtectedRoute>} />
<Route path="/nutrition" element={<ProtectedRoute><Nutrition /></ProtectedRoute>} />
<Route path="/programs" element={<ProtectedRoute><ProgramSearch /></ProtectedRoute>} />
<Route path="/programs/:id" element={<ProtectedRoute><ProgramDetail /></ProtectedRoute>} />
<Route path="/catalog/:id" element={<ProtectedRoute><CatalogDetail /></ProtectedRoute>} />
<Route path="/rewards" element={<ProtectedRoute><Rewards /></ProtectedRoute>} />
<Route path="/week" element={<ProtectedRoute><WeekView /></ProtectedRoute>} />
<Route path="/settings" element={<ProtectedRoute><Settings /></ProtectedRoute>} />
<Route path="/support" element={<ProtectedRoute><Support /></ProtectedRoute>} />
<Route path="/support/admin" element={<ProtectedRoute><SupportAdmin /></ProtectedRoute>} />
<Route path="/support/admin/:threadId" element={<ProtectedRoute><SupportAdmin /></ProtectedRoute>} />
<Route path="/welcome" element={<Welcome />} />
<Route path="/settings/integrations" element={<ProtectedRoute><SettingsIntegrations /></ProtectedRoute>} />
<Route path="/meal-plan" element={<ProtectedRoute><MealPlan /></ProtectedRoute>} />
<Route path="/chat" element={<ProtectedRoute><ChatCoach /></ProtectedRoute>} />
<Route path="/agent" element={<ProtectedRoute><AgentConnect /></ProtectedRoute>} />
</Routes>
{hasToken() && <NavBar />}
</>
)
}