|
6 | 6 | import { useState, useEffect, useCallback } from 'react' |
7 | 7 | import { useNavigate } from 'react-router' |
8 | 8 | import { useAuthStore } from '../auth/store' |
9 | | -import { fetchInstallations } from './dashboardApi' |
10 | | -import type { InstallationSummary } from './dashboardApi' |
| 9 | +import { fetchInstallations, fetchUserStats } from './dashboardApi' |
| 10 | +import type { InstallationSummary, UserStats } from './dashboardApi' |
11 | 11 | import { Card, Chip, Dot, Overline, StatCard } from '../../shared/components' |
| 12 | +import ActivityChart from './ActivityChart' |
12 | 13 |
|
13 | 14 | const INSTALL_URL = `https://github.com/apps/${import.meta.env.VITE_GITHUB_APP_SLUG ?? 'helprs'}/installations/new` |
14 | 15 |
|
15 | 16 | export default function InstallationList() { |
16 | 17 | const navigate = useNavigate() |
17 | 18 | const user = useAuthStore((s) => s.user) |
18 | 19 | const [installations, setInstallations] = useState<InstallationSummary[]>([]) |
| 20 | + const [stats, setStats] = useState<UserStats | null>(null) |
19 | 21 | const [loading, setLoading] = useState(true) |
20 | 22 | const [error, setError] = useState<string | null>(null) |
21 | 23 |
|
22 | 24 | const load = useCallback(async () => { |
23 | 25 | setLoading(true) |
24 | 26 | try { |
25 | | - const data = await fetchInstallations() |
26 | | - setInstallations(data.items) |
| 27 | + const [instData, statsData] = await Promise.all([ |
| 28 | + fetchInstallations(), |
| 29 | + fetchUserStats().catch(() => null), |
| 30 | + ]) |
| 31 | + setInstallations(instData.items) |
| 32 | + setStats(statsData) |
27 | 33 | } catch { |
28 | 34 | setError('Failed to load installations') |
29 | 35 | } finally { |
@@ -57,10 +63,18 @@ export default function InstallationList() { |
57 | 63 | <div className="grid grid-cols-2 md:grid-cols-4 gap-3 mb-8"> |
58 | 64 | <StatCard label="Installations" value={installations.length} /> |
59 | 65 | <StatCard label="Configured" value={configured} color={configured > 0 ? 'ok' : 'warn'} sub={`of ${installations.length}`} /> |
60 | | - <StatCard label="Total sessions" value={totalSessions} color="accent" /> |
61 | | - <StatCard label="Status" value={error ? 'Error' : 'Live'} color={error ? 'danger' : 'ok'} /> |
| 66 | + <StatCard label="Total sessions" value={stats?.totals.total ?? totalSessions} color="accent" /> |
| 67 | + <StatCard label="Completed" value={stats?.totals.completed ?? 0} color="ok" /> |
62 | 68 | </div> |
63 | 69 |
|
| 70 | + {/* Activity chart */} |
| 71 | + {stats && stats.daily_counts.length > 0 && ( |
| 72 | + <Card className="mb-8 px-4 py-3"> |
| 73 | + <Overline className="mb-3">{'\u25b8'} ACTIVITY {'\u00b7'} last 30 days</Overline> |
| 74 | + <ActivityChart data={stats.daily_counts} /> |
| 75 | + </Card> |
| 76 | + )} |
| 77 | + |
64 | 78 | {/* Error */} |
65 | 79 | {error && ( |
66 | 80 | <Card className="mb-6 border-danger/30"> |
|
0 commit comments