File size: 5,234 Bytes
49e53ae
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
'use client'

import { useMemo } from 'react'
import { 
  LineChart, Line, XAxis, YAxis, CartesianGrid, Tooltip, 
  ResponsiveContainer, Area, AreaChart, ReferenceLine, Scatter, ComposedChart
} from 'recharts'
import { TrendingUp } from 'lucide-react'

interface Transaction {
  id: number
  amount: number
  prediction: string
  final_score: number
}

interface TransactionChartProps {
  transactions: Transaction[]
}

export default function TransactionChart({ transactions }: TransactionChartProps) {
  const chartData = useMemo(() => {
    if (transactions.length === 0) return []
    
    // Create rolling average for smoother visualization
    const windowSize = Math.max(3, Math.floor(transactions.length / 50))
    
    return transactions.map((t, i) => {
      const start = Math.max(0, i - windowSize + 1)
      const window = transactions.slice(start, i + 1)
      const avgAmount = window.reduce((sum, tx) => sum + tx.amount, 0) / window.length
      
      return {
        index: i,
        amount: t.amount,
        smoothAmount: avgAmount,
        score: t.final_score,
        isFraud: t.prediction === 'Fraud',
        id: t.id
      }
    })
  }, [transactions])

  const CustomTooltip = ({ active, payload, label }: any) => {
    if (active && payload && payload.length) {
      const data = payload[0].payload
      return (
        <div className="glass rounded-lg p-3 border border-dark-600">

          <p className="text-sm text-dark-300">Transaction #{data.id}</p>

          <p className="text-lg font-semibold">${data.amount.toFixed(2)}</p>

          <p className="text-sm">

            Risk Score: <span className={data.score > 0.5 ? 'text-red-400' : 'text-green-400'}>

              {(data.score * 100).toFixed(1)}%

            </span>

          </p>

          {data.isFraud && (

            <p className="text-red-400 text-sm font-medium mt-1">⚠️ Fraud Detected</p>

          )}

        </div>
      )
    }
    return null
  }

  if (transactions.length === 0) {
    return (
      <div className="glass rounded-xl p-6 h-80 flex flex-col items-center justify-center">

        <TrendingUp className="w-12 h-12 text-dark-500 mb-4" />

        <p className="text-dark-400 text-center">

          Start processing transactions to see the flow chart

        </p>

      </div>
    )
  }

  return (
    <div className="glass rounded-xl p-6">

      <h3 className="text-lg font-semibold mb-4 flex items-center gap-2">

        <TrendingUp className="w-5 h-5 text-primary-500" />

        Transaction Flow Analysis

        <span className="text-sm font-normal text-dark-400 ml-2">

          ({transactions.length.toLocaleString()} transactions)

        </span>

      </h3>

      

      <div className="h-64">

        <ResponsiveContainer width="100%" height="100%">

          <ComposedChart data={chartData}>

            <defs>

              <linearGradient id="amountGradient" x1="0" y1="0" x2="0" y2="1">

                <stop offset="5%" stopColor="#00d4a0" stopOpacity={0.3} />

                <stop offset="95%" stopColor="#00d4a0" stopOpacity={0} />

              </linearGradient>

            </defs>

            

            <CartesianGrid strokeDasharray="3 3" stroke="#333" />

            

            <XAxis 

              dataKey="index" 

              stroke="#666"

              tick={{ fill: '#888', fontSize: 11 }}

              tickFormatter={(value) => `#${value}`}

            />

            

            <YAxis 

              stroke="#666"

              tick={{ fill: '#888', fontSize: 11 }}

              tickFormatter={(value) => `$${value}`}

            />

            

            <Tooltip content={<CustomTooltip />} />

            

            {/* Smooth area chart */}

            <Area

              type="monotone"

              dataKey="smoothAmount"

              stroke="#00d4a0"

              strokeWidth={2}

              fill="url(#amountGradient)"

            />

            

            {/* Fraud markers */}

            <Scatter

              dataKey="amount"

              fill="#ff4444"

              shape={(props: any) => {

                if (!props.payload.isFraud) return <circle r={0} />

                return (

                  <circle 

                    cx={props.cx} 

                    cy={props.cy} 

                    r={6} 

                    fill="#ff4444" 

                    stroke="#fff" 

                    strokeWidth={2}

                  />

                )

              }}

            />

          </ComposedChart>

        </ResponsiveContainer>

      </div>

      

      {/* Legend */}

      <div className="flex items-center gap-6 mt-4 justify-center">

        <div className="flex items-center gap-2">

          <div className="w-4 h-1 bg-primary-500 rounded" />

          <span className="text-sm text-dark-300">Transaction Flow</span>

        </div>

        <div className="flex items-center gap-2">

          <div className="w-3 h-3 bg-red-500 rounded-full" />

          <span className="text-sm text-dark-300">Fraud Alert</span>

        </div>

      </div>

    </div>
  )
}