1+ import React , { useState , useEffect } from 'react' ;
2+ import PropTypes from 'prop-types' ;
3+ import '../../styles/TransactionAnalytics.css' ;
4+
5+ /**
6+ * TransactionAnalytics component
7+ * Displays transaction success rates, timing analytics, and trust indicators
8+ * Provides transparency and builds user confidence
9+ */
10+ const TransactionAnalytics = ( {
11+ userStats = { } ,
12+ globalStats = { } ,
13+ recentTransactions = [ ] ,
14+ showPersonalStats = true ,
15+ showGlobalStats = true ,
16+ showRecentActivity = true ,
17+ timeframe = '7d' ,
18+ compact = false ,
19+ onTimeframeChange = null
20+ } ) => {
21+ const [ selectedTimeframe , setSelectedTimeframe ] = useState ( timeframe ) ;
22+ const [ isExpanded , setIsExpanded ] = useState ( ! compact ) ;
23+
24+ // Calculate statistics
25+ const calculateStats = ( transactions ) => {
26+ if ( ! transactions || transactions . length === 0 ) {
27+ return {
28+ total : 0 ,
29+ successful : 0 ,
30+ failed : 0 ,
31+ pending : 0 ,
32+ successRate : 0 ,
33+ averageTime : 0 ,
34+ totalValue : 0
35+ } ;
36+ }
37+
38+ const total = transactions . length ;
39+ const successful = transactions . filter ( tx => tx . status === 'success' ) . length ;
40+ const failed = transactions . filter ( tx => tx . status === 'error' ) . length ;
41+ const pending = transactions . filter ( tx => tx . status === 'pending' ) . length ;
42+ const successRate = total > 0 ? Math . round ( ( successful / total ) * 100 ) : 0 ;
43+
44+ const completedTxs = transactions . filter ( tx => tx . status === 'success' && tx . duration ) ;
45+ const averageTime = completedTxs . length > 0
46+ ? Math . round ( completedTxs . reduce ( ( sum , tx ) => sum + tx . duration , 0 ) / completedTxs . length )
47+ : 0 ;
48+
49+ const totalValue = transactions
50+ . filter ( tx => tx . status === 'success' && tx . value )
51+ . reduce ( ( sum , tx ) => sum + tx . value , 0 ) ;
52+
53+ return {
54+ total,
55+ successful,
56+ failed,
57+ pending,
58+ successRate,
59+ averageTime,
60+ totalValue
61+ } ;
62+ } ;
63+
64+ const stats = calculateStats ( recentTransactions ) ;
65+
66+ // Handle timeframe change
67+ const handleTimeframeChange = ( newTimeframe ) => {
68+ setSelectedTimeframe ( newTimeframe ) ;
69+ if ( onTimeframeChange ) {
70+ onTimeframeChange ( newTimeframe ) ;
71+ }
72+ } ;
73+
74+ // Format value display
75+ const formatValue = ( value ) => {
76+ if ( value >= 1000000 ) return `${ ( value / 1000000 ) . toFixed ( 1 ) } M` ;
77+ if ( value >= 1000 ) return `${ ( value / 1000 ) . toFixed ( 1 ) } K` ;
78+ return value . toFixed ( 2 ) ;
79+ } ;
80+
81+ // Format time display
82+ const formatTime = ( seconds ) => {
83+ if ( seconds < 60 ) return `${ seconds } s` ;
84+ const minutes = Math . floor ( seconds / 60 ) ;
85+ const remainingSeconds = seconds % 60 ;
86+ return `${ minutes } m ${ remainingSeconds } s` ;
87+ } ;
88+
89+ // Get success rate color
90+ const getSuccessRateColor = ( rate ) => {
91+ if ( rate >= 95 ) return '#10b981' ;
92+ if ( rate >= 85 ) return '#f59e0b' ;
93+ return '#ef4444' ;
94+ } ;
95+
96+ // Get status indicator
97+ const getStatusIndicator = ( rate ) => {
98+ if ( rate >= 95 ) return '🟢' ;
99+ if ( rate >= 85 ) return '🟡' ;
100+ return '🔴' ;
101+ } ;
102+
103+ if ( compact ) {
104+ return (
105+ < div className = "transaction-analytics-compact" >
106+ < div className = "stats-summary" >
107+ < div className = "stat-item" >
108+ < span className = "stat-value" style = { { color : getSuccessRateColor ( stats . successRate ) } } >
109+ { stats . successRate } %
110+ </ span >
111+ < span className = "stat-label" > Success Rate</ span >
112+ </ div >
113+
114+ < div className = "stat-item" >
115+ < span className = "stat-value" > { stats . total } </ span >
116+ < span className = "stat-label" > Total Transactions</ span >
117+ </ div >
118+
119+ { stats . averageTime > 0 && (
120+ < div className = "stat-item" >
121+ < span className = "stat-value" > { formatTime ( stats . averageTime ) } </ span >
122+ < span className = "stat-label" > Avg Time</ span >
123+ </ div >
124+ ) }
125+ </ div >
126+
127+ </ div >
128+ ) ;
129+ }
130+
131+ return (
132+ < div className = "transaction-analytics" >
133+ < div className = "analytics-header" >
134+ < h3 className = "analytics-title" > Transaction Analytics</ h3 >
135+
136+ < div className = "analytics-controls" >
137+ < div className = "timeframe-selector" >
138+ { [ '24h' , '7d' , '30d' , '90d' ] . map ( ( tf ) => (
139+ < button
140+ key = { tf }
141+ className = { `timeframe-button ${ tf === selectedTimeframe ? 'active' : '' } ` }
142+ onClick = { ( ) => handleTimeframeChange ( tf ) }
143+ >
144+ { tf }
145+ </ button >
146+ ) ) }
147+ </ div >
148+
149+ < button
150+ className = "expand-button"
151+ onClick = { ( ) => setIsExpanded ( ! isExpanded ) }
152+ >
153+ { isExpanded ? '▲' : '▼' }
154+ </ button >
155+ </ div >
156+ </ div >
157+
158+ { isExpanded && (
159+ < div className = "analytics-content" >
160+ { /* Overall Success Rate */ }
161+ < div className = "success-rate-card" >
162+ < div className = "success-rate-header" >
163+ < h4 > Overall Success Rate</ h4 >
164+ < span className = "status-indicator" >
165+ { getStatusIndicator ( stats . successRate ) }
166+ </ span >
167+ </ div >
168+
169+ < div className = "success-rate-display" >
170+ < div className = "rate-circle" >
171+ < svg viewBox = "0 0 36 36" className = "circular-chart" >
172+ < path
173+ className = "circle-bg"
174+ d = "M18 2.0845
175+ a 15.9155 15.9155 0 0 1 0 31.831
176+ a 15.9155 15.9155 0 0 1 0 -31.831"
177+ />
178+ < path
179+ className = "circle"
180+ strokeDasharray = { `${ stats . successRate } , 100` }
181+ d = "M18 2.0845
182+ a 15.9155 15.9155 0 0 1 0 31.831
183+ a 15.9155 15.9155 0 0 1 0 -31.831"
184+ style = { { stroke : getSuccessRateColor ( stats . successRate ) } }
185+ />
186+ </ svg >
187+ < div className = "rate-text" >
188+ < span className = "rate-percentage" > { stats . successRate } %</ span >
189+ < span className = "rate-label" > Success</ span >
190+ </ div >
191+ </ div >
192+
193+ < div className = "rate-breakdown" >
194+ < div className = "breakdown-item success" >
195+ < span className = "breakdown-count" > { stats . successful } </ span >
196+ < span className = "breakdown-label" > Successful</ span >
197+ </ div >
198+ < div className = "breakdown-item failed" >
199+ < span className = "breakdown-count" > { stats . failed } </ span >
200+ < span className = "breakdown-label" > Failed</ span >
201+ </ div >
202+ { stats . pending > 0 && (
203+ < div className = "breakdown-item pending" >
204+ < span className = "breakdown-count" > { stats . pending } </ span >
205+ < span className = "breakdown-label" > Pending</ span >
206+ </ div >
207+ ) }
208+ </ div >
209+ </ div >
210+ </ div >
211+
212+ { /* Performance Metrics */ }
213+ < div className = "metrics-grid" >
214+ < div className = "metric-card" >
215+ < div className = "metric-icon" > ⚡</ div >
216+ < div className = "metric-content" >
217+ < span className = "metric-value" >
218+ { stats . averageTime > 0 ? formatTime ( stats . averageTime ) : 'N/A' }
219+ </ span >
220+ < span className = "metric-label" > Average Completion Time</ span >
221+ </ div >
222+ </ div >
223+
224+ < div className = "metric-card" >
225+ < div className = "metric-icon" > 💰</ div >
226+ < div className = "metric-content" >
227+ < span className = "metric-value" >
228+ { stats . totalValue > 0 ? `$${ formatValue ( stats . totalValue ) } ` : 'N/A' }
229+ </ span >
230+ < span className = "metric-label" > Total Value Processed</ span >
231+ </ div >
232+ </ div >
233+
234+ < div className = "metric-card" >
235+ < div className = "metric-icon" > 🔄</ div >
236+ < div className = "metric-content" >
237+ < span className = "metric-value" > { stats . total } </ span >
238+ < span className = "metric-label" > Total Transactions</ span >
239+ </ div >
240+ </ div >
241+
242+ { globalStats . networkHealth && (
243+ < div className = "metric-card" >
244+ < div className = "metric-icon" > 🌐</ div >
245+ < div className = "metric-content" >
246+ < span className = "metric-value" > { globalStats . networkHealth } %</ span >
247+ < span className = "metric-label" > Network Health</ span >
248+ </ div >
249+ </ div >
250+ ) }
251+ </ div >
252+
253+ { /* Trust Indicators */ }
254+ < div className = "trust-section" >
255+ < h4 > Trust & Security Indicators </ h4 >
256+ < div className = "trust-indicators" >
257+ < div className = "trust-item" >
258+ < span className = "trust-icon" > 🔒</ span >
259+ < span className = "trust-text" > End-to-end encryption</ span >
260+ < span className = "trust-status verified" > ✓</ span >
261+ </ div >
262+
263+ < div className = "trust-item" >
264+ < span className = "trust-icon" > 🛡️</ span >
265+ < span className = "trust-text" > Multi-signature verification</ span >
266+ < span className = "trust-status verified" > ✓</ span >
267+ </ div >
268+
269+ < div className = "trust-item" >
270+ < span className = "trust-icon" > ⚡</ span >
271+ < span className = "trust-text" > Lightning-fast processing</ span >
272+ < span className = "trust-status verified" > ✓</ span >
273+ </ div >
274+
275+ < div className = "trust-item" >
276+ < span className = "trust-icon" > 📊</ span >
277+ < span className = "trust-text" > Real-time monitoring</ span >
278+ < span className = "trust-status verified" > ✓</ span >
279+ </ div >
280+ </ div >
281+ </ div >
282+
283+ { /* Recent Activity Preview */ }
284+ { showRecentActivity && recentTransactions . length > 0 && (
285+ < div className = "recent-activity" >
286+ < h4 > Recent Transaction Activity</ h4 >
287+ < div className = "activity-list" >
288+ { recentTransactions . slice ( 0 , 5 ) . map ( ( tx , index ) => (
289+ < div key = { index } className = { `activity-item ${ tx . status } ` } >
290+ < div className = "activity-status" >
291+ { tx . status === 'success' ? '✓' :
292+ tx . status === 'error' ? '✗' : '⏳' }
293+ </ div >
294+ < div className = "activity-details" >
295+ < span className = "activity-type" > { tx . type || 'Transaction' } </ span >
296+ < span className = "activity-time" >
297+ { new Date ( tx . timestamp ) . toLocaleTimeString ( ) }
298+ </ span >
299+ </ div >
300+ { tx . value && (
301+ < div className = "activity-value" >
302+ ${ formatValue ( tx . value ) }
303+ </ div >
304+ ) }
305+ </ div >
306+ ) ) }
307+ </ div >
308+ </ div >
309+ ) }
310+ </ div >
311+ ) }
312+
313+ </ div >
314+ ) ;
315+ } ;
316+
317+ TransactionAnalytics . propTypes = {
318+ userStats : PropTypes . object ,
319+ globalStats : PropTypes . object ,
320+ recentTransactions : PropTypes . arrayOf (
321+ PropTypes . shape ( {
322+ id : PropTypes . string ,
323+ type : PropTypes . string ,
324+ status : PropTypes . oneOf ( [ 'success' , 'error' , 'pending' ] ) . isRequired ,
325+ timestamp : PropTypes . string . isRequired ,
326+ duration : PropTypes . number ,
327+ value : PropTypes . number
328+ } )
329+ ) ,
330+ showPersonalStats : PropTypes . bool ,
331+ showGlobalStats : PropTypes . bool ,
332+ showRecentActivity : PropTypes . bool ,
333+ timeframe : PropTypes . oneOf ( [ '24h' , '7d' , '30d' , '90d' ] ) ,
334+ compact : PropTypes . bool ,
335+ onTimeframeChange : PropTypes . func
336+ } ;
337+
338+ export default TransactionAnalytics ;
0 commit comments