@@ -10,6 +10,7 @@ import {
1010import { network , appDetails , userSession } from '../utils/stacks' ;
1111import { CONTRACT_ADDRESS , CONTRACT_NAME } from '../config/contracts' ;
1212import { formatSTX } from '../lib/utils' ;
13+ import { analytics } from '../lib/analytics' ;
1314
1415export default function AdminDashboard ( { addToast } ) {
1516 const [ stats , setStats ] = useState ( null ) ;
@@ -19,6 +20,7 @@ export default function AdminDashboard({ addToast }) {
1920 const [ loading , setLoading ] = useState ( true ) ;
2021 const [ actionLoading , setActionLoading ] = useState ( false ) ;
2122 const [ isOwner , setIsOwner ] = useState ( false ) ;
23+ const [ analyticsData , setAnalyticsData ] = useState ( null ) ;
2224
2325 const userAddress = userSession . isUserSignedIn ( )
2426 ? userSession . loadUserData ( ) . profile . stxAddress . mainnet
@@ -72,6 +74,7 @@ export default function AdminDashboard({ addToast }) {
7274
7375 useEffect ( ( ) => {
7476 fetchAdminData ( ) ;
77+ setAnalyticsData ( analytics . getSummary ( ) ) ;
7578 } , [ fetchAdminData ] ) ;
7679
7780 const handlePauseToggle = async ( ) => {
@@ -157,6 +160,104 @@ export default function AdminDashboard({ addToast }) {
157160 ) ;
158161 }
159162
163+ const AnalyticsPanel = ( ) => {
164+ if ( ! analyticsData ) return null ;
165+ const a = analyticsData ;
166+
167+ return (
168+ < div className = "bg-white p-6 rounded-2xl shadow-sm border border-gray-100 space-y-5" >
169+ < div className = "flex items-center justify-between" >
170+ < h3 className = "text-lg font-bold text-gray-800" > Usage Analytics</ h3 >
171+ < button
172+ onClick = { ( ) => setAnalyticsData ( analytics . getSummary ( ) ) }
173+ className = "text-xs text-gray-500 hover:text-gray-700 font-medium"
174+ >
175+ Refresh
176+ </ button >
177+ </ div >
178+
179+ < div className = "grid grid-cols-2 sm:grid-cols-4 gap-3" >
180+ < div className = "bg-gray-50 rounded-xl p-3 text-center" >
181+ < p className = "text-2xl font-black text-gray-900" > { a . totalPageViews } </ p >
182+ < p className = "text-xs text-gray-500 mt-0.5" > Page Views</ p >
183+ </ div >
184+ < div className = "bg-gray-50 rounded-xl p-3 text-center" >
185+ < p className = "text-2xl font-black text-gray-900" > { a . walletConnections } </ p >
186+ < p className = "text-xs text-gray-500 mt-0.5" > Wallet Connects</ p >
187+ </ div >
188+ < div className = "bg-gray-50 rounded-xl p-3 text-center" >
189+ < p className = "text-2xl font-black text-gray-900" > { a . sessions } </ p >
190+ < p className = "text-xs text-gray-500 mt-0.5" > Sessions</ p >
191+ </ div >
192+ < div className = "bg-gray-50 rounded-xl p-3 text-center" >
193+ < p className = "text-2xl font-black text-gray-900" > { a . totalErrors } </ p >
194+ < p className = "text-xs text-gray-500 mt-0.5" > Errors</ p >
195+ </ div >
196+ </ div >
197+
198+ < div >
199+ < h4 className = "text-sm font-semibold text-gray-700 mb-2" > Tip Funnel</ h4 >
200+ < div className = "space-y-1.5" >
201+ { [
202+ [ 'Started' , a . tipsStarted ] ,
203+ [ 'Submitted' , a . tipsSubmitted ] ,
204+ [ 'Confirmed' , a . tipsConfirmed ] ,
205+ [ 'Cancelled' , a . tipsCancelled ] ,
206+ [ 'Failed' , a . tipsFailed ] ,
207+ ] . map ( ( [ label , count ] ) => (
208+ < div key = { label } className = "flex items-center justify-between text-sm" >
209+ < span className = "text-gray-600" > { label } </ span >
210+ < span className = "font-semibold text-gray-900" > { count } </ span >
211+ </ div >
212+ ) ) }
213+ < div className = "border-t border-gray-100 pt-1.5 flex items-center justify-between text-sm" >
214+ < span className = "text-gray-600" > Completion Rate</ span >
215+ < span className = "font-bold text-green-600" > { a . tipCompletionRate } %</ span >
216+ </ div >
217+ < div className = "flex items-center justify-between text-sm" >
218+ < span className = "text-gray-600" > Drop-off Rate</ span >
219+ < span className = "font-bold text-orange-500" > { a . tipDropOffRate } %</ span >
220+ </ div >
221+ </ div >
222+ </ div >
223+
224+ { a . sortedTabs . length > 0 && (
225+ < div >
226+ < h4 className = "text-sm font-semibold text-gray-700 mb-2" > Tab Navigation</ h4 >
227+ < div className = "space-y-1" >
228+ { a . sortedTabs . map ( ( [ tab , count ] ) => (
229+ < div key = { tab } className = "flex items-center justify-between text-sm" >
230+ < span className = "text-gray-600 font-mono text-xs" > { tab } </ span >
231+ < span className = "font-semibold text-gray-900" > { count } </ span >
232+ </ div >
233+ ) ) }
234+ </ div >
235+ </ div >
236+ ) }
237+
238+ { a . sortedErrors . length > 0 && (
239+ < div >
240+ < h4 className = "text-sm font-semibold text-gray-700 mb-2" > Top Errors</ h4 >
241+ < div className = "space-y-1" >
242+ { a . sortedErrors . map ( ( [ error , count ] ) => (
243+ < div key = { error } className = "flex items-center justify-between text-sm" >
244+ < span className = "text-gray-600 truncate max-w-[70%]" title = { error } > { error } </ span >
245+ < span className = "font-semibold text-red-600" > { count } </ span >
246+ </ div >
247+ ) ) }
248+ </ div >
249+ </ div >
250+ ) }
251+
252+ { a . firstSeen && (
253+ < p className = "text-xs text-gray-400 text-right" >
254+ Tracking since { new Date ( a . firstSeen ) . toLocaleDateString ( ) }
255+ </ p >
256+ ) }
257+ </ div >
258+ ) ;
259+ } ;
260+
160261 return (
161262 < div className = "max-w-2xl mx-auto space-y-6" >
162263 < h2 className = "text-2xl font-bold text-gray-800" > Admin Dashboard</ h2 >
@@ -223,6 +324,8 @@ export default function AdminDashboard({ addToast }) {
223324 </ button >
224325 </ div >
225326 </ div >
327+
328+ < AnalyticsPanel />
226329 </ div >
227330 ) ;
228331}
0 commit comments