1+ 'use client' ;
2+
3+ import { useAuth } from '@/contexts/AuthContext' ;
4+ import { analyticsAPI } from '@/lib/api' ;
5+ import { useRouter } from 'next/navigation' ;
6+ import { useEffect , useState } from 'react' ;
7+
8+ export default function CertificateAnalyticsPage ( ) {
9+ const { user } = useAuth ( ) ;
10+ const router = useRouter ( ) ;
11+ const [ analytics , setAnalytics ] = useState < any > ( null ) ;
12+ const [ isLoading , setIsLoading ] = useState ( true ) ;
13+
14+ useEffect ( ( ) => {
15+ if ( ! user ) {
16+ router . push ( '/auth/login' ) ;
17+ return ;
18+ }
19+
20+ async function loadAnalytics ( ) {
21+ try {
22+ const data = await analyticsAPI . getUserAnalytics ( user ! . id ) ;
23+ setAnalytics ( data ) ;
24+ } catch ( error ) {
25+ console . error ( 'Failed to load analytics:' , error ) ;
26+ } finally {
27+ setIsLoading ( false ) ;
28+ }
29+ }
30+
31+ loadAnalytics ( ) ;
32+ } , [ user , router ] ) ;
33+
34+ if ( isLoading ) {
35+ return (
36+ < div className = "flex min-h-[calc(100vh-80px)] items-center justify-center bg-black" >
37+ < div className = "text-center" >
38+ < div className = "mx-auto mb-4 h-16 w-16 animate-spin rounded-full border-4 border-red-600/30 border-t-red-600" > </ div >
39+ < p className = "font-mono text-sm tracking-widest text-red-500 uppercase" >
40+ Loading Analytics...
41+ </ p >
42+ </ div >
43+ </ div >
44+ ) ;
45+ }
46+
47+ if ( ! analytics ) {
48+ return (
49+ < div className = "flex min-h-[calc(100vh-80px)] items-center justify-center bg-black" >
50+ < div className = "text-center" >
51+ < p className = "font-mono text-sm tracking-widest text-red-500 uppercase" >
52+ No analytics data available
53+ </ p >
54+ </ div >
55+ </ div >
56+ ) ;
57+ }
58+
59+ return (
60+ < div className = "relative min-h-[calc(100vh-80px)] overflow-hidden bg-black p-8 text-white md:p-12" >
61+ { /* Background Effect */ }
62+ < div className = "pointer-events-none absolute top-0 right-0 h-[500px] w-[500px] rounded-full bg-red-600/10 blur-[100px]" > </ div >
63+
64+ < div className = "relative z-10 mx-auto max-w-7xl" >
65+ < div className = "mb-12 border-l-4 border-red-600 py-2 pl-6" >
66+ < h1 className = "mb-2 text-4xl font-black tracking-tight uppercase md:text-5xl" >
67+ Certificate < span className = "text-red-500" > Analytics</ span >
68+ </ h1 >
69+ < p className = "font-mono text-lg text-sm font-light tracking-wide text-gray-400 uppercase" >
70+ Performance metrics for Operator: < span className = "text-white" > { user ?. name } </ span >
71+ </ p >
72+ </ div >
73+
74+ < div className = "grid grid-cols-1 gap-8 md:grid-cols-2 lg:grid-cols-4" >
75+ < div className = "rounded-2xl border border-white/10 bg-zinc-950 p-6 shadow-xl" >
76+ < h3 className = "mb-4 text-sm font-bold tracking-widest text-white uppercase" >
77+ Total Certificates
78+ </ h3 >
79+ < p className = "text-3xl font-black text-white" > { analytics . certificatesCount || 0 } </ p >
80+ </ div >
81+
82+ < div className = "rounded-2xl border border-white/10 bg-zinc-950 p-6 shadow-xl" >
83+ < h3 className = "mb-4 text-sm font-bold tracking-widest text-white uppercase" >
84+ Active Certificates
85+ </ h3 >
86+ < p className = "text-3xl font-black text-white" > { analytics . certificates ?. filter ( ( c : any ) => c . status === 'ACTIVE' ) ?. length || 0 } </ p >
87+ </ div >
88+
89+ < div className = "rounded-2xl border border-white/10 bg-zinc-950 p-6 shadow-xl" >
90+ < h3 className = "mb-4 text-sm font-bold tracking-widest text-white uppercase" >
91+ Revoked Certificates
92+ </ h3 >
93+ < p className = "text-3xl font-black text-white" > { analytics . certificates ?. filter ( ( c : any ) => c . status === 'REVOKED' ) ?. length || 0 } </ p >
94+ </ div >
95+
96+ < div className = "rounded-2xl border border-white/10 bg-zinc-950 p-6 shadow-xl" >
97+ < h3 className = "mb-4 text-sm font-bold tracking-widest text-white uppercase" >
98+ Revocation Rate
99+ </ h3 >
100+ < p className = "text-3xl font-black text-white" > { analytics . certificates ? ( ( analytics . certificates . filter ( ( c : any ) => c . status === 'REVOKED' ) . length / analytics . certificates . length ) * 100 ) . toFixed ( 1 ) + '%' : '0%' } </ p >
101+ </ div >
102+ </ div >
103+
104+ < div className = "mt-8 rounded-2xl border border-white/10 bg-zinc-950 p-6 shadow-xl" >
105+ < h3 className = "mb-6 border-b border-white/10 pb-4 text-sm font-bold tracking-widest text-white uppercase" >
106+ Certificate Status Distribution
107+ </ h3 >
108+
109+ < div className = "space-y-4" >
110+ { analytics . certificates && analytics . certificates . length > 0 ? (
111+ Object . entries (
112+ analytics . certificates . reduce ( ( acc : Record < string , number > , cert : any ) => {
113+ acc [ cert . status ] = ( acc [ cert . status ] || 0 ) + 1 ;
114+ return acc ;
115+ } , { } as Record < string , number > )
116+ ) . map ( ( [ status , count ] ) => (
117+ < div key = { status } className = "flex items-center justify-between rounded border border-white/5 bg-black p-3" >
118+ < span className = "text-gray-500 capitalize" > { status } </ span >
119+ < span className = "font-bold text-white" > { count } </ span >
120+ </ div >
121+ ) )
122+ ) : (
123+ < div className = "flex items-center justify-between rounded border border-white/5 bg-black p-3" >
124+ < span className = "text-gray-500" > No certificates</ span >
125+ < span className = "font-bold text-white" > 0</ span >
126+ </ div >
127+ ) }
128+ </ div >
129+ </ div >
130+ </ div >
131+ </ div >
132+ ) ;
133+ }
0 commit comments