11'use client' ;
22
33import { useMemo } from 'react' ;
4- import { motion } from 'framer-motion' ;
4+ import { motion , Variants } from 'framer-motion' ;
55import {
66 TrendingUp ,
77 TrendingDown ,
@@ -15,6 +15,7 @@ import {
1515 ThumbsUp ,
1616 GitBranch ,
1717} from 'lucide-react' ;
18+ import { Card , CardContent , CardHeader } from '@/components/ui/card' ;
1819import { GetMeResponse } from '@/lib/api/types' ;
1920import { calculateChartTrend , TrendResult } from '@/lib/utils/calculateTrend' ;
2021
@@ -28,7 +29,7 @@ function TrendBadge({ trend }: { trend: TrendResult }) {
2829 return (
2930 < span
3031 aria-label = { `Up ${ trend . percentage } %` }
31- className = 'inline-flex items-center gap-1 rounded-full bg-emerald-500/15 px-2 py-0.5 text-xs font-medium text-emerald-400 '
32+ className = 'bg-primary/15 text-primary inline-flex items-center gap-1 rounded-full px-2 py-0.5 text-xs font-medium'
3233 >
3334 < TrendingUp className = 'h-3 w-3' aria-hidden = 'true' />
3435 { trend . percentage } %
@@ -49,7 +50,7 @@ function TrendBadge({ trend }: { trend: TrendResult }) {
4950 return (
5051 < span
5152 aria-label = 'No change'
52- className = 'inline-flex items-center gap-1 rounded-full bg-zinc-700/50 px-2 py-0.5 text-xs font-medium text-zinc-400 '
53+ className = 'bg-muted text-muted-foreground inline-flex items-center gap-1 rounded-full px-2 py-0.5 text-xs font-medium'
5354 >
5455 < Minus className = 'h-3 w-3' aria-hidden = 'true' />
5556 0%
@@ -64,28 +65,26 @@ interface TileConfig {
6465 trend : TrendResult ;
6566 colSpan : string ;
6667 rowSpan : string ;
67- gradient : string ;
6868 large ?: boolean ;
6969}
7070
71- const FLAT_TREND : TrendResult = { percentage : 0 , direction : 'flat' } ;
72-
73- const containerVariants = {
71+ const containerVariants : Variants = {
7472 hidden : { } ,
7573 show : { transition : { staggerChildren : 0.07 } } ,
7674} ;
7775
78- const tileVariants = {
76+ const tileVariants : Variants = {
7977 hidden : { opacity : 0 , y : 16 } ,
8078 show : {
8179 opacity : 1 ,
8280 y : 0 ,
83- transition : { duration : 0.35 , ease : 'easeOut' as const } ,
81+ transition : { duration : 0.35 , ease : [ 0.4 , 0 , 0.2 , 1 ] as const } ,
8482 } ,
8583} ;
8684
8785export function AnalyticsBentoGrid ( { stats, chart } : Props ) {
8886 const chartTrend = useMemo ( ( ) => calculateChartTrend ( chart ) , [ chart ] ) ;
87+ const flat : TrendResult = { percentage : 0 , direction : 'flat' } ;
8988
9089 const tiles : TileConfig [ ] = useMemo (
9190 ( ) => [
@@ -96,8 +95,6 @@ export function AnalyticsBentoGrid({ stats, chart }: Props) {
9695 trend : chartTrend ,
9796 colSpan : 'col-span-2' ,
9897 rowSpan : 'row-span-2' ,
99- gradient :
100- 'bg-gradient-to-br from-[#06b6d4]/20 via-[#4f46e5]/10 to-transparent' ,
10198 large : true ,
10299 } ,
103100 {
@@ -107,77 +104,70 @@ export function AnalyticsBentoGrid({ stats, chart }: Props) {
107104 trend : chartTrend ,
108105 colSpan : 'col-span-1' ,
109106 rowSpan : 'row-span-1' ,
110- gradient : 'bg-white/[0.03]' ,
111107 } ,
112108 {
113109 label : 'Projects Created' ,
114110 value : stats . projectsCreated ,
115111 icon : < FolderGit2 className = 'h-4 w-4' /> ,
116- trend : FLAT_TREND ,
112+ trend : flat ,
117113 colSpan : 'col-span-1' ,
118114 rowSpan : 'row-span-1' ,
119- gradient : 'bg-white/[0.03]' ,
120115 } ,
121116 {
122117 label : 'Hackathons Entered' ,
123118 value : stats . hackathons ,
124119 icon : < Trophy className = 'h-4 w-4' /> ,
125- trend : FLAT_TREND ,
120+ trend : flat ,
126121 colSpan : 'col-span-1' ,
127122 rowSpan : 'row-span-1' ,
128- gradient : 'bg-gradient-to-br from-amber-500/10 to-transparent' ,
129123 } ,
130124 {
131125 label : 'Followers' ,
132126 value : stats . followers ,
133127 icon : < Users className = 'h-4 w-4' /> ,
134- trend : FLAT_TREND ,
128+ trend : flat ,
135129 colSpan : 'col-span-1' ,
136130 rowSpan : 'row-span-1' ,
137- gradient : 'bg-white/[0.03]' ,
138131 } ,
139132 {
140133 label : 'Total Contributed' ,
141134 value : stats . totalContributed ,
142135 icon : < DollarSign className = 'h-4 w-4' /> ,
143- trend : FLAT_TREND ,
136+ trend : flat ,
144137 colSpan : 'col-span-1' ,
145138 rowSpan : 'row-span-1' ,
146- gradient : 'bg-gradient-to-br from-emerald-500/10 to-transparent' ,
147139 } ,
148140 {
149141 label : 'Comments Posted' ,
150142 value : stats . commentsPosted ,
151143 icon : < MessageSquare className = 'h-4 w-4' /> ,
152- trend : FLAT_TREND ,
144+ trend : flat ,
153145 colSpan : 'col-span-1' ,
154146 rowSpan : 'row-span-1' ,
155- gradient : 'bg-white/[0.03]' ,
156147 } ,
157148 {
158149 label : 'Votes Cast' ,
159150 value : stats . votes ,
160151 icon : < ThumbsUp className = 'h-4 w-4' /> ,
161- trend : FLAT_TREND ,
152+ trend : flat ,
162153 colSpan : 'col-span-1' ,
163154 rowSpan : 'row-span-1' ,
164- gradient : 'bg-white/[0.03]' ,
165155 } ,
166156 {
167157 label : 'Following' ,
168158 value : stats . following ,
169159 icon : < GitBranch className = 'h-4 w-4' /> ,
170- trend : FLAT_TREND ,
160+ trend : flat ,
171161 colSpan : 'col-span-1' ,
172162 rowSpan : 'row-span-1' ,
173- gradient : 'bg-white/[0.03]' ,
174163 } ,
175164 ] ,
176- [ stats , chartTrend ]
165+ [ stats , chartTrend , flat ]
177166 ) ;
178167
179168 return (
180169 < >
170+ { /* Screen reader fallback table */ }
181171 < table className = 'sr-only' aria-label = 'Analytics statistics' >
182172 < thead >
183173 < tr >
@@ -206,29 +196,32 @@ export function AnalyticsBentoGrid({ stats, chart }: Props) {
206196 variants = { containerVariants }
207197 initial = 'hidden'
208198 animate = 'show'
209- className = 'grid grid-cols-2 gap-3 sm:grid-cols-4'
199+ className = 'grid grid-cols-2 gap-4 sm:grid-cols-4'
210200 >
211201 { tiles . map ( tile => (
212202 < motion . div
213203 key = { tile . label }
214204 variants = { tileVariants }
215- className = { `relative flex flex-col justify-between rounded-2xl border border-white/[0.06] p-5 backdrop-blur-sm transition-colors duration-200 hover:border-white/10 ${ tile . gradient } ${ tile . colSpan } ${ tile . rowSpan } ` }
205+ className = { `${ tile . colSpan } ${ tile . rowSpan } ` }
216206 >
217- < div className = 'pointer-events-none absolute inset-0 rounded-2xl bg-gradient-to-br from-white/[0.04] to-transparent' />
218- < div className = 'flex items-start justify-between' >
219- < div className = 'flex h-9 w-9 items-center justify-center rounded-xl bg-white/[0.06] text-zinc-300' >
220- { tile . icon }
221- </ div >
222- < TrendBadge trend = { tile . trend } />
223- </ div >
224- < div className = 'mt-4' >
225- < p
226- className = { `font-bold tracking-tight text-white ${ tile . large ? 'text-4xl' : 'text-2xl' } ` }
227- >
228- { tile . value . toLocaleString ( ) }
229- </ p >
230- < p className = 'mt-1 text-sm text-zinc-500' > { tile . label } </ p >
231- </ div >
207+ < Card className = 'h-full' >
208+ < CardHeader className = 'flex flex-row items-start justify-between space-y-0 pb-2' >
209+ < div className = 'bg-primary/10 text-primary flex h-9 w-9 items-center justify-center rounded-xl' >
210+ { tile . icon }
211+ </ div >
212+ < TrendBadge trend = { tile . trend } />
213+ </ CardHeader >
214+ < CardContent >
215+ < p
216+ className = { `font-bold tracking-tight ${ tile . large ? 'text-4xl' : 'text-2xl' } ` }
217+ >
218+ { tile . value . toLocaleString ( ) }
219+ </ p >
220+ < p className = 'text-muted-foreground mt-1 text-sm' >
221+ { tile . label }
222+ </ p >
223+ </ CardContent >
224+ </ Card >
232225 </ motion . div >
233226 ) ) }
234227 </ motion . div >
0 commit comments