+
+ ← Back to Integrations
+
)
diff --git a/frontend/src/pages/Dashboard.jsx b/frontend/src/pages/Dashboard.jsx
index 6227b83..e3b3f63 100644
--- a/frontend/src/pages/Dashboard.jsx
+++ b/frontend/src/pages/Dashboard.jsx
@@ -32,6 +32,7 @@ function Tip({ text, children }) {
export default function Dashboard() {
const [status, setStatus] = useState(null)
const [integrations, setIntegrations] = useState(null)
+ const [aiStatus, setAiStatus] = useState(null)
const [loading, setLoading] = useState(true)
const [syncing, setSyncing] = useState(null)
const navigate = useNavigate()
@@ -39,9 +40,10 @@ export default function Dashboard() {
useEffect(() => { loadAll() }, [])
async function loadAll() {
try {
- const [s, intg] = await Promise.all([api.today(), api.integrationStatus()])
+ const [s, intg, ai] = await Promise.all([api.today(), api.integrationStatus(), api.getAIStatus().catch(() => ({ configured: false }))])
setStatus(s)
setIntegrations(intg)
+ setAiStatus(ai)
} catch (err) { console.error(err) }
finally { setLoading(false) }
}
@@ -83,8 +85,77 @@ export default function Dashboard() {
{ key: 'daily_checkin', label: 'Check-in', icon: '✅', pts: 10, color: '#00BCD4', tip: 'Just show up and check in. The easiest points — consistency matters more than intensity.' },
]
+ // Getting-started checklist — shows until dismissed
+ const [showChecklist, setShowChecklist] = useState(() => {
+ return localStorage.getItem('diligence_setup_dismissed') !== 'true'
+ })
+
+ const setupItems = [
+ { done: true, label: 'Create account', link: null },
+ { done: !!status.program_name, label: 'Join a program', link: '/programs' },
+ { done: aiStatus?.configured, label: 'Connect an AI coach', link: '/settings/integrations#ai-coaching' },
+ { done: Object.values(integrations || {}).some(v => v?.connected), label: 'Connect a fitness device', link: '/settings/integrations#fitness-devices' },
+ { done: false, label: 'Set up rewards', link: '/rewards' },
+ ]
+ const completedCount = setupItems.filter(i => i.done).length
+ const allDone = completedCount === setupItems.length
+
return (
+ {/* Getting started checklist */}
+ {showChecklist && !allDone && (
+
+
+
+ Getting Started
+
+
+
+ {setupItems.map((item, i) => (
+
item.link && !item.done && navigate(item.link)}
+ style={{
+ display: 'flex', alignItems: 'center', gap: '10px',
+ padding: '7px 0',
+ cursor: item.link && !item.done ? 'pointer' : 'default',
+ }}
+ >
+
+ {item.done ? '✓' : ''}
+
+
+ {item.label}
+
+ {item.link && !item.done && (
+ ›
+ )}
+
+ ))}
+
+ {completedCount} of {setupItems.length} complete
+
+
+ )}
+
{/* Program bar */}
{status.program_name && (
›
+
navigate('/agent')}
+ style={{
+ display: 'flex', alignItems: 'center', justifyContent: 'space-between',
+ padding: '14px 0', cursor: 'pointer',
+ }}
+ >
+
+
🤖
+
+
Connect AI Agent
+
Claude Desktop, Cursor, Claude Code setup
+
+
+
›
+
{/* Point Rules */}
@@ -192,7 +208,7 @@ export default function Settings() {
onClick={() => navigate('/settings/integrations')}
style={{ padding: '12px 0', textAlign: 'center', cursor: 'pointer', color: 'var(--accent)', fontSize: '0.88rem', fontWeight: 600 }}
>
- Configure all 11 providers →
+ All integrations →
diff --git a/frontend/src/pages/SettingsIntegrations.jsx b/frontend/src/pages/SettingsIntegrations.jsx
index 28b8f1d..fb10d66 100644
--- a/frontend/src/pages/SettingsIntegrations.jsx
+++ b/frontend/src/pages/SettingsIntegrations.jsx
@@ -1,4 +1,5 @@
import { useState, useEffect } from 'react'
+import { useLocation } from 'react-router-dom'
import { api } from '../api'
const STATUS_COLORS = {
@@ -13,16 +14,51 @@ const STATUS_LABELS = {
not_configured: 'Not configured',
}
+const CATEGORY_ORDER = ['device', 'ai_provider', 'nutrition', 'notifications']
+
+const CATEGORY_INFO = {
+ device: {
+ title: 'Fitness Devices',
+ description: 'Sync workouts automatically from your watch or tracker.',
+ },
+ ai_provider: {
+ title: 'AI Coaching',
+ description: 'Connect an LLM to power the built-in AI coach in the Coach tab. One provider is enough.',
+ },
+ nutrition: {
+ title: 'Nutrition',
+ description: 'Food databases for accurate macro tracking.',
+ },
+ notifications: {
+ title: 'Notifications',
+ description: 'Get alerts when things happen.',
+ },
+}
+
+const SYNC_BADGE = {
+ coming_soon: { label: 'Coming soon', color: 'var(--text-3)' },
+ mcp_bridge: { label: 'MCP bridge', color: 'var(--accent)' },
+}
+
export default function SettingsIntegrations() {
const [providers, setProviders] = useState({})
const [status, setStatus] = useState({})
const [loading, setLoading] = useState(true)
- const [configuring, setConfiguring] = useState(null) // provider key being configured
+ const [configuring, setConfiguring] = useState(null)
const [formData, setFormData] = useState({})
const [saving, setSaving] = useState(false)
+ const location = useLocation()
useEffect(() => { loadAll() }, [])
+ // Handle hash scrolling (e.g. /settings/integrations#ai-coaching)
+ useEffect(() => {
+ if (!loading && location.hash) {
+ const el = document.getElementById(location.hash.slice(1))
+ if (el) setTimeout(() => el.scrollIntoView({ behavior: 'smooth', block: 'start' }), 100)
+ }
+ }, [loading, location.hash])
+
async function loadAll() {
try {
const [p, s] = await Promise.all([api.listProviders(), api.fullIntegrationStatus()])
@@ -50,88 +86,152 @@ export default function SettingsIntegrations() {
if (loading) return