@@ -13,7 +13,7 @@ import {
1313 AreaChart ,
1414 Area ,
1515} from 'recharts' ;
16- import { BarChart3 , Download , FileSearch , Loader2 , PieChart as PieChartIcon } from 'lucide-react' ;
16+ import { BarChart3 , Download , FileSearch , Loader2 , PieChart as PieChartIcon , RefreshCw } from 'lucide-react' ;
1717import { Card , CardContent , CardHeader , CardTitle } from '@/components/ui/card' ;
1818import { Select , SelectContent , SelectItem , SelectTrigger , SelectValue } from '@/components/ui/select' ;
1919
@@ -40,24 +40,96 @@ export default function AnalyticsDashboard() {
4040 const [ filterTheme , setFilterTheme ] = useState < string > ( 'all' ) ;
4141 const [ filterTime , setFilterTime ] = useState < string > ( 'all' ) ;
4242 const [ chartType , setChartType ] = useState < string > ( 'bar' ) ;
43+ const [ refreshKey , setRefreshKey ] = useState ( 0 ) ;
44+
45+ // Function to clear corrupted data
46+ const clearCorruptedData = ( ) => {
47+ localStorage . removeItem ( 'componentAnalytics' ) ;
48+ setAnalyticsData ( [ ] ) ;
49+ setRefreshKey ( prev => prev + 1 ) ;
50+ } ;
4351
4452 useEffect ( ( ) => {
4553 try {
4654 const data = typeof window !== 'undefined' ? localStorage . getItem ( 'componentAnalytics' ) : null ;
4755 if ( data ) {
48- setAnalyticsData ( JSON . parse ( data ) ) ;
56+ const parsedData = JSON . parse ( data ) ;
57+ console . log ( 'Parsed analytics data:' , parsedData , 'Type:' , typeof parsedData , 'Is Array:' , Array . isArray ( parsedData ) ) ;
58+ // Ensure parsedData is an array
59+ if ( Array . isArray ( parsedData ) ) {
60+ setAnalyticsData ( parsedData ) ;
61+ } else {
62+ console . warn ( 'Analytics data is not an array, initializing with empty array. Data:' , parsedData ) ;
63+ setAnalyticsData ( [ ] ) ;
64+ }
65+ } else {
66+ // Add sample data if no real data exists
67+ const sampleData : AnalyticsEvent [ ] = [
68+ {
69+ component : "Button" ,
70+ event : "copy" ,
71+ theme : "dark" ,
72+ timestamp : new Date ( Date . now ( ) - 1 * 24 * 60 * 60 * 1000 ) . toISOString ( ) , // 1 day ago
73+ } ,
74+ {
75+ component : "Button" ,
76+ event : "copy" ,
77+ theme : "light" ,
78+ timestamp : new Date ( Date . now ( ) - 2 * 24 * 60 * 60 * 1000 ) . toISOString ( ) , // 2 days ago
79+ } ,
80+ {
81+ component : "Input" ,
82+ event : "copy" ,
83+ theme : "dark" ,
84+ timestamp : new Date ( Date . now ( ) - 3 * 24 * 60 * 60 * 1000 ) . toISOString ( ) , // 3 days ago
85+ } ,
86+ {
87+ component : "Card" ,
88+ event : "copy" ,
89+ theme : "light" ,
90+ timestamp : new Date ( Date . now ( ) - 5 * 24 * 60 * 60 * 1000 ) . toISOString ( ) , // 5 days ago
91+ } ,
92+ {
93+ component : "Button" ,
94+ event : "copy" ,
95+ theme : "dark" ,
96+ timestamp : new Date ( Date . now ( ) - 7 * 24 * 60 * 60 * 1000 ) . toISOString ( ) , // 7 days ago
97+ } ,
98+ {
99+ component : "Dialog" ,
100+ event : "copy" ,
101+ theme : "light" ,
102+ timestamp : new Date ( Date . now ( ) - 10 * 24 * 60 * 60 * 1000 ) . toISOString ( ) , // 10 days ago
103+ } ,
104+ {
105+ component : "Select" ,
106+ event : "copy" ,
107+ theme : "dark" ,
108+ timestamp : new Date ( Date . now ( ) - 15 * 24 * 60 * 60 * 1000 ) . toISOString ( ) , // 15 days ago
109+ } ,
110+ {
111+ component : "Badge" ,
112+ event : "copy" ,
113+ theme : "light" ,
114+ timestamp : new Date ( Date . now ( ) - 20 * 24 * 60 * 60 * 1000 ) . toISOString ( ) , // 20 days ago
115+ } ,
116+ ] ;
117+ setAnalyticsData ( sampleData ) ;
49118 }
50119 } catch ( error ) {
51120 // keep console.error for debugging in dev
52121 // eslint-disable-next-line no-console
53122 console . error ( 'Error reading analytics data:' , error ) ;
123+ // Set empty array as fallback
124+ setAnalyticsData ( [ ] ) ;
54125 } finally {
55126 setIsLoading ( false ) ;
56127 }
57- } , [ ] ) ;
128+ } , [ refreshKey ] ) ;
58129
59130 // Filter and aggregate data for charts
60- const filteredData = analyticsData . filter ( ( event ) => {
131+ console . log ( 'analyticsData before filter:' , analyticsData , 'Type:' , typeof analyticsData , 'Is Array:' , Array . isArray ( analyticsData ) ) ;
132+ const filteredData = ( Array . isArray ( analyticsData ) ? analyticsData : [ ] ) . filter ( ( event ) => {
61133 const componentMatch = filterComponent === 'all' || event . component === filterComponent ;
62134 const themeMatch = filterTheme === 'all' || event . theme === filterTheme ;
63135 const timeMatch =
@@ -119,9 +191,15 @@ export default function AnalyticsDashboard() {
119191 return (
120192 < div className = "min-h-screen bg-gradient-to-br from-background to-muted/50 dark:from-background dark:to-muted/30 py-6 px-4 sm:px-6 lg:px-8" >
121193 < div className = "max-w-7xl mx-auto space-y-6" >
122- < h1 className = "text-2xl sm:text-3xl font-bold text-foreground tracking-tight animate-in fade-in duration-500" >
123- Analytics Dashboard
124- </ h1 >
194+ < div className = "flex flex-col sm:flex-row sm:items-center sm:justify-between gap-4" >
195+ < h1 className = "text-2xl sm:text-3xl font-bold text-foreground tracking-tight animate-in fade-in duration-500" >
196+ Analytics Dashboard
197+ </ h1 >
198+ < div className = "flex items-center gap-4 text-sm text-muted-foreground" >
199+ < span > Total Events: { Array . isArray ( analyticsData ) ? analyticsData . length : 0 } </ span >
200+ < span > Components: { Array . isArray ( analyticsData ) ? new Set ( analyticsData . map ( d => d . component ) ) . size : 0 } </ span >
201+ </ div >
202+ </ div >
125203
126204 { /* Filters Card */ }
127205 < Card className = "border-none shadow-sm" >
@@ -140,11 +218,11 @@ export default function AnalyticsDashboard() {
140218 < SelectItem value = "all" key = "all" >
141219 All Components
142220 </ SelectItem >
143- { [ ...new Set ( analyticsData . map ( ( d ) => d . component ) ) ] . map ( ( comp ) => (
221+ { Array . isArray ( analyticsData ) ? [ ...new Set ( analyticsData . map ( ( d ) => d . component ) ) ] . map ( ( comp ) => (
144222 < SelectItem key = { comp } value = { comp } >
145223 { comp }
146224 </ SelectItem >
147- ) ) }
225+ ) ) : null }
148226 </ SelectContent >
149227 </ Select >
150228 </ div >
@@ -202,9 +280,60 @@ export default function AnalyticsDashboard() {
202280 </ button >
203281 </ div >
204282 </ div >
283+
284+ { /* Sample Data Notice */ }
285+ { Array . isArray ( analyticsData ) && analyticsData . length > 0 && analyticsData [ 0 ] ?. component === "Button" && analyticsData [ 0 ] ?. timestamp . includes ( "2024" ) && (
286+ < div className = "mt-4 p-3 bg-blue-50 dark:bg-blue-950/20 border border-blue-200 dark:border-blue-800 rounded-lg" >
287+ < p className = "text-sm text-blue-800 dark:text-blue-200" >
288+ 📊 < strong > Demo Mode:</ strong > This is sample data. Copy some components from the home page to see real analytics!
289+ </ p >
290+ < button
291+ className = "mt-2 text-xs text-blue-600 dark:text-blue-400 hover:underline"
292+ onClick = { ( ) => {
293+ localStorage . removeItem ( 'componentAnalytics' ) ;
294+ window . location . reload ( ) ;
295+ } }
296+ >
297+ Clear sample data
298+ </ button >
299+ </ div >
300+ ) }
205301 </ CardContent >
206302 </ Card >
207303
304+ { /* Recent Activity */ }
305+ { Array . isArray ( analyticsData ) && analyticsData . length > 0 && (
306+ < Card className = "border-none shadow-sm" >
307+ < CardHeader >
308+ < CardTitle className = "text-lg font-semibold text-foreground" > Recent Activity</ CardTitle >
309+ </ CardHeader >
310+ < CardContent >
311+ < div className = "space-y-2" >
312+ { analyticsData
313+ . sort ( ( a , b ) => new Date ( b . timestamp ) . getTime ( ) - new Date ( a . timestamp ) . getTime ( ) )
314+ . slice ( 0 , 5 )
315+ . map ( ( event , index ) => (
316+ < div key = { index } className = "flex items-center justify-between p-3 bg-muted/30 rounded-lg" >
317+ < div className = "flex items-center gap-3" >
318+ < div className = "w-2 h-2 bg-primary rounded-full" > </ div >
319+ < span className = "font-medium" > { event . component } </ span >
320+ < span className = "text-sm text-muted-foreground" > copied</ span >
321+ </ div >
322+ < div className = "flex items-center gap-2 text-sm text-muted-foreground" >
323+ < span className = { `px-2 py-1 rounded-full text-xs ${
324+ event . theme === 'dark' ? 'bg-gray-800 text-gray-200' : 'bg-gray-200 text-gray-800'
325+ } `} >
326+ { event . theme }
327+ </ span >
328+ < span > { new Date ( event . timestamp ) . toLocaleString ( ) } </ span >
329+ </ div >
330+ </ div >
331+ ) ) }
332+ </ div >
333+ </ CardContent >
334+ </ Card >
335+ ) }
336+
208337 { /* Loading State */ }
209338 { isLoading ? (
210339 < Card className = "border-none shadow-sm" >
@@ -222,7 +351,22 @@ export default function AnalyticsDashboard() {
222351 < Card className = "border-none shadow-sm animate-in fade-in zoom-in-95 duration-500" >
223352 < CardHeader className = "flex flex-col space-y-2 md:flex-row md:space-y-0 md:items-center md:justify-between" >
224353 < CardTitle className = "text-lg font-semibold text-foreground" > Most Copied Components</ CardTitle >
225- < Select value = { chartType } onValueChange = { setChartType } >
354+ < div className = "flex items-center gap-2" >
355+ < button
356+ onClick = { ( ) => setRefreshKey ( prev => prev + 1 ) }
357+ className = "p-2 hover:bg-muted rounded-md transition-colors"
358+ title = "Refresh data"
359+ >
360+ < RefreshCw className = "h-4 w-4" />
361+ </ button >
362+ < button
363+ onClick = { clearCorruptedData }
364+ className = "p-2 hover:bg-muted rounded-md transition-colors text-red-500 hover:text-red-700"
365+ title = "Clear all data"
366+ >
367+ 🗑️
368+ </ button >
369+ < Select value = { chartType } onValueChange = { setChartType } >
226370 < SelectTrigger
227371 aria-label = "Select chart type"
228372 className = "h-9 w-full md:w-32 bg-background/80 hover:bg-background transition-colors duration-200 border-border/50 focus:ring-2 focus:ring-primary focus:ring-offset-1"
@@ -249,14 +393,20 @@ export default function AnalyticsDashboard() {
249393 </ div >
250394 </ SelectItem >
251395 </ SelectContent >
252- </ Select >
396+ </ Select >
397+ </ div >
253398 </ CardHeader >
254399
255400 < CardContent >
256401 { copyChartData . length === 0 ? (
257402 < div className = "flex flex-col gap-y-4 items-center justify-center h-[200px] w-full" >
258403 < FileSearch className = "size-6 text-muted-foreground" />
259- < p className = "text-muted-foreground text-sm" > No data for this period</ p >
404+ < div className = "text-center space-y-2" >
405+ < p className = "text-muted-foreground text-sm" > No data for this period</ p >
406+ < p className = "text-xs text-muted-foreground/70" >
407+ Try adjusting your filters or copy some components from the home page
408+ </ p >
409+ </ div >
260410 </ div >
261411 ) : (
262412 < div className = "space-y-6" >
0 commit comments