Skip to content

Commit f0c645c

Browse files
committed
feat: enhance dashboard metrics with average duration and session counts
1 parent 6cb6b61 commit f0c645c

6 files changed

Lines changed: 100 additions & 113 deletions

File tree

src/app/dashboard/page.tsx

Lines changed: 40 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -21,10 +21,40 @@ import { formatDistanceToNow } from '@/lib/utils';
2121
export default function DashboardPage() {
2222
const { data: sessions, isLoading } = useSessions();
2323

24-
// Calculate stats
24+
// Calculate real stats from actual session data
2525
const totalSessions = sessions?.length || 0;
2626
const recentSessions = sessions?.slice(0, 5) || [];
2727

28+
// Calculate average duration from sessions with fileInfo
29+
const avgDuration = (() => {
30+
const sessionsWithDuration = sessions?.filter(s => s.fileInfo?.durationSec) || [];
31+
if (sessionsWithDuration.length === 0) return '--';
32+
33+
const totalSeconds = sessionsWithDuration.reduce((sum, s) => sum + (s.fileInfo?.durationSec || 0), 0);
34+
const avgSeconds = totalSeconds / sessionsWithDuration.length;
35+
const minutes = Math.floor(avgSeconds / 60);
36+
const seconds = Math.floor(avgSeconds % 60);
37+
return `${minutes}:${String(seconds).padStart(2, '0')}`;
38+
})();
39+
40+
// Calculate this month's sessions
41+
const thisMonthSessions = (() => {
42+
if (!sessions) return 0;
43+
const now = new Date();
44+
const startOfMonth = new Date(now.getFullYear(), now.getMonth(), 1);
45+
return sessions.filter(s => new Date(s.createdAt) >= startOfMonth).length;
46+
})();
47+
48+
// Calculate average LUFS from completed sessions
49+
const avgLUFS = (() => {
50+
const sessionsWithLUFS = sessions?.filter(s => s.analysis?.loudness?.integratedLUFS) || [];
51+
if (sessionsWithLUFS.length === 0) return '--';
52+
53+
const totalLUFS = sessionsWithLUFS.reduce((sum, s) => sum + (s.analysis?.loudness?.integratedLUFS || 0), 0);
54+
const avg = totalLUFS / sessionsWithLUFS.length;
55+
return avg.toFixed(1);
56+
})();
57+
2858
return (
2959
<div className="space-y-8">
3060
{/* Page Header */}
@@ -52,36 +82,39 @@ export default function DashboardPage() {
5282
<Card>
5383
<CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
5484
<CardTitle className="text-sm font-medium text-gray-600">Avg. Duration</CardTitle>
55-
<TrendingUp className="h-4 w-4 text-gray-400" />
85+
<Clock className="h-4 w-4 text-gray-400" />
5686
</CardHeader>
5787
<CardContent>
5888
<div className="text-2xl font-bold">
59-
{isLoading ? <Skeleton className="h-8 w-16" /> : '3:45'}
89+
{isLoading ? <Skeleton className="h-8 w-16" /> : avgDuration}
6090
</div>
91+
<p className="text-xs text-gray-500 mt-1">Average file length</p>
6192
</CardContent>
6293
</Card>
6394

6495
<Card>
6596
<CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
6697
<CardTitle className="text-sm font-medium text-gray-600">This Month</CardTitle>
67-
<Clock className="h-4 w-4 text-gray-400" />
98+
<TrendingUp className="h-4 w-4 text-gray-400" />
6899
</CardHeader>
69100
<CardContent>
70101
<div className="text-2xl font-bold">
71-
{isLoading ? <Skeleton className="h-8 w-16" /> : totalSessions}
102+
{isLoading ? <Skeleton className="h-8 w-16" /> : thisMonthSessions}
72103
</div>
104+
<p className="text-xs text-gray-500 mt-1">Sessions created</p>
73105
</CardContent>
74106
</Card>
75107

76108
<Card>
77109
<CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
78-
<CardTitle className="text-sm font-medium text-gray-600">Analysis Score</CardTitle>
110+
<CardTitle className="text-sm font-medium text-gray-600">Avg. LUFS</CardTitle>
79111
<BarChart3 className="h-4 w-4 text-gray-400" />
80112
</CardHeader>
81113
<CardContent>
82114
<div className="text-2xl font-bold">
83-
{isLoading ? <Skeleton className="h-8 w-16" /> : '92.5'}
115+
{isLoading ? <Skeleton className="h-8 w-16" /> : avgLUFS}
84116
</div>
117+
<p className="text-xs text-gray-500 mt-1">Average loudness</p>
85118
</CardContent>
86119
</Card>
87120
</div>

src/app/sessions/[id]/page.tsx

Lines changed: 4 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -55,10 +55,11 @@ export default function SessionDetailPage() {
5555
const tonalBalance = useMemo(() => {
5656
const spectrum = data?.analysis?.spectrum;
5757
if (!spectrum) return [];
58+
// Spectrum values are 0-1 scale, convert to percentages for display
5859
return [
59-
{ band: 'Low', value: spectrum.low },
60-
{ band: 'Mid', value: spectrum.mids },
61-
{ band: 'High', value: spectrum.highs },
60+
{ band: 'Low', value: Math.round(spectrum.low * 100) },
61+
{ band: 'Mid', value: Math.round(spectrum.mids * 100) },
62+
{ band: 'High', value: Math.round(spectrum.highs * 100) },
6263
];
6364
}, [data]);
6465

@@ -236,7 +237,6 @@ export default function SessionDetailPage() {
236237
<TabsContent value="analysis" className="space-y-6">
237238
{metrics.length > 0 && <SessionMetrics metrics={metrics} />}
238239
<SessionCharts
239-
loudnessTrend={buildTrend(data.analysis?.loudness)}
240240
tonalBalance={tonalBalance.length ? tonalBalance : defaultBalance()}
241241
stereoWidth={data.analysis?.stereo?.widthScore ?? 0}
242242
/>
@@ -254,17 +254,6 @@ export default function SessionDetailPage() {
254254
);
255255
}
256256

257-
function buildTrend(loudness?: { integratedLUFS: number }) {
258-
if (!loudness) return [];
259-
return [
260-
{ time: 'Intro', value: loudness.integratedLUFS - 2 },
261-
{ time: 'Verse', value: loudness.integratedLUFS - 1 },
262-
{ time: 'Chorus', value: loudness.integratedLUFS },
263-
{ time: 'Bridge', value: loudness.integratedLUFS - 1 },
264-
{ time: 'Outro', value: loudness.integratedLUFS - 0.5 },
265-
];
266-
}
267-
268257
function defaultBalance() {
269258
return [
270259
{ band: 'Low', value: 40 },

src/components/dashboard/recent-sessions.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ export function RecentSessions({ sessions }: RecentSessionsProps) {
4747
{sessions.map((session) => (
4848
<Card
4949
key={session.id}
50-
className="border border-indigo-100 bg-white shadow-sm transition hover:border-indigo-300 hover:shadow-md"
50+
className="border border-indigo-100 bg-white transition hover:border-indigo-300"
5151
>
5252
<CardHeader className="flex flex-row items-center justify-between space-y-0">
5353
<div>

src/components/session/feedback-panel.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ interface FeedbackPanelProps {
1010

1111
export function FeedbackPanel({ summary, suggestions }: FeedbackPanelProps) {
1212
return (
13-
<Card className="h-full border border-indigo-100 bg-white shadow-md">
13+
<Card className="h-full border border-indigo-100 bg-white">
1414
<CardHeader>
1515
<CardTitle className="text-lg font-bold text-gray-900">AI Feedback</CardTitle>
1616
</CardHeader>
Lines changed: 53 additions & 88 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,11 @@
11
'use client';
22

33
import {
4-
LineChart,
5-
Line,
4+
BarChart,
5+
Bar,
66
XAxis,
77
Tooltip,
88
ResponsiveContainer,
9-
BarChart,
10-
Bar,
119
RadialBarChart,
1210
RadialBar,
1311
PolarAngleAxis,
@@ -16,7 +14,6 @@ import {
1614
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
1715

1816
interface SessionChartsProps {
19-
loudnessTrend: { time: string; value: number }[];
2017
tonalBalance: { band: string; value: number }[];
2118
stereoWidth: number;
2219
}
@@ -31,104 +28,72 @@ const tooltipStyles = {
3128
labelStyle: { color: '#4b5563', fontWeight: 600 },
3229
};
3330

34-
export function SessionCharts({ loudnessTrend, tonalBalance, stereoWidth }: SessionChartsProps) {
31+
export function SessionCharts({ tonalBalance, stereoWidth }: SessionChartsProps) {
3532
return (
36-
<div className="space-y-6">
37-
<Card className="border border-indigo-100 bg-white shadow-md">
33+
<div className="grid gap-6 md:grid-cols-2">
34+
<Card className="border border-indigo-100 bg-white">
3835
<CardHeader>
39-
<CardTitle className="text-lg font-bold text-gray-900">Loudness over time</CardTitle>
36+
<CardTitle className="text-lg font-bold text-gray-900">Tonal Balance</CardTitle>
4037
</CardHeader>
4138
<CardContent className="h-64">
4239
<ResponsiveContainer width="100%" height="100%">
43-
<LineChart data={loudnessTrend}>
40+
<BarChart data={tonalBalance} barSize={32}>
4441
<XAxis
45-
dataKey="time"
42+
dataKey="band"
4643
stroke="#6b7280"
4744
tickLine={false}
4845
axisLine={false}
4946
style={{ fontSize: 12 }}
5047
/>
51-
<Tooltip
52-
{...tooltipStyles}
53-
formatter={(value: number) => `${value.toFixed(1)} LUFS`}
54-
/>
55-
<Line
56-
type="monotone"
57-
dataKey="value"
58-
stroke="#4f46e5"
59-
strokeWidth={3}
60-
dot={false}
61-
activeDot={{ r: 6, fill: '#6366f1', strokeWidth: 0 }}
62-
/>
63-
</LineChart>
48+
<Tooltip {...tooltipStyles} formatter={(value: number) => `${value}%`} />
49+
<Bar dataKey="value" radius={[8, 8, 0, 0]}>
50+
{tonalBalance.map((_, index) => (
51+
<Cell
52+
key={`cell-${index}`}
53+
fill={index === 0 ? '#4f46e5' : index === 1 ? '#6366f1' : '#8b5cf6'}
54+
/>
55+
))}
56+
</Bar>
57+
</BarChart>
6458
</ResponsiveContainer>
6559
</CardContent>
6660
</Card>
67-
<div className="grid gap-6 md:grid-cols-2">
68-
<Card className="border border-indigo-100 bg-white shadow-md">
69-
<CardHeader>
70-
<CardTitle className="text-lg font-bold text-gray-900">Tonal Balance</CardTitle>
71-
</CardHeader>
72-
<CardContent className="h-64">
73-
<ResponsiveContainer width="100%" height="100%">
74-
<BarChart data={tonalBalance} barSize={32}>
75-
<XAxis
76-
dataKey="band"
77-
stroke="#6b7280"
78-
tickLine={false}
79-
axisLine={false}
80-
style={{ fontSize: 12 }}
81-
/>
82-
<Tooltip {...tooltipStyles} formatter={(value: number) => `${value}%`} />
83-
<Bar dataKey="value" radius={[8, 8, 0, 0]}>
84-
{tonalBalance.map((_, index) => (
85-
<Cell
86-
key={`cell-${index}`}
87-
fill={index === 0 ? '#4f46e5' : index === 1 ? '#6366f1' : '#8b5cf6'}
88-
/>
89-
))}
90-
</Bar>
91-
</BarChart>
92-
</ResponsiveContainer>
93-
</CardContent>
94-
</Card>
95-
<Card className="border border-indigo-100 bg-white shadow-md">
96-
<CardHeader>
97-
<CardTitle className="text-lg font-bold text-gray-900">Stereo Image</CardTitle>
98-
</CardHeader>
99-
<CardContent className="flex h-64 items-center justify-center">
100-
<ResponsiveContainer width="100%" height="100%">
101-
<RadialBarChart
102-
data={[{ name: 'Width', value: stereoWidth * 100 }]}
103-
innerRadius="75%"
104-
outerRadius="105%"
105-
startAngle={90}
106-
endAngle={-270}
61+
<Card className="border border-indigo-100 bg-white">
62+
<CardHeader>
63+
<CardTitle className="text-lg font-bold text-gray-900">Stereo Image</CardTitle>
64+
</CardHeader>
65+
<CardContent className="flex h-64 items-center justify-center">
66+
<ResponsiveContainer width="100%" height="100%">
67+
<RadialBarChart
68+
data={[{ name: 'Width', value: stereoWidth * 100 }]}
69+
innerRadius="75%"
70+
outerRadius="105%"
71+
startAngle={90}
72+
endAngle={-270}
73+
>
74+
<PolarAngleAxis type="number" domain={[0, 100]} tick={false} />
75+
<RadialBar cornerRadius={12} dataKey="value" fill="url(#stereoGradient)" />
76+
<defs>
77+
<linearGradient id="stereoGradient" x1="0%" y1="0%" x2="100%" y2="100%">
78+
<stop offset="0%" stopColor="#4f46e5" />
79+
<stop offset="100%" stopColor="#8b5cf6" />
80+
</linearGradient>
81+
</defs>
82+
<text
83+
x="50%"
84+
y="50%"
85+
textAnchor="middle"
86+
dominantBaseline="middle"
87+
fill="#1f2937"
88+
fontSize={24}
89+
fontWeight={700}
10790
>
108-
<PolarAngleAxis type="number" domain={[0, 100]} tick={false} />
109-
<RadialBar cornerRadius={12} dataKey="value" fill="url(#stereoGradient)" />
110-
<defs>
111-
<linearGradient id="stereoGradient" x1="0%" y1="0%" x2="100%" y2="100%">
112-
<stop offset="0%" stopColor="#4f46e5" />
113-
<stop offset="100%" stopColor="#8b5cf6" />
114-
</linearGradient>
115-
</defs>
116-
<text
117-
x="50%"
118-
y="50%"
119-
textAnchor="middle"
120-
dominantBaseline="middle"
121-
fill="#1f2937"
122-
fontSize={24}
123-
fontWeight={700}
124-
>
125-
{(stereoWidth * 100).toFixed(0)}%
126-
</text>
127-
</RadialBarChart>
128-
</ResponsiveContainer>
129-
</CardContent>
130-
</Card>
131-
</div>
91+
{(stereoWidth * 100).toFixed(0)}%
92+
</text>
93+
</RadialBarChart>
94+
</ResponsiveContainer>
95+
</CardContent>
96+
</Card>
13297
</div>
13398
);
13499
}

src/components/session/session-metrics.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ export function SessionMetrics({ metrics }: SessionMetricsProps) {
1919
{metrics.map((metric, index) => {
2020
const Icon = icons[index] ?? Gauge;
2121
return (
22-
<Card key={metric.label} className="border border-indigo-100 bg-white shadow-md">
22+
<Card key={metric.label} className="border border-indigo-100 bg-white">
2323
<CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
2424
<CardTitle className="text-sm font-semibold uppercase tracking-wider text-gray-600">
2525
{metric.label}

0 commit comments

Comments
 (0)