File size: 4,975 Bytes
8fa21e1
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
import { useState } from 'react';
import {
    LineChart, Line, XAxis, YAxis, Tooltip,
    Legend, ResponsiveContainer, ReferenceLine
} from 'recharts';

const CLUSTER_COLORS = [
    '#f59e0b', '#3b82f6', '#22c55e', '#ef4444',
    '#a855f7', '#06b6d4', '#f97316', '#ec4899',
    '#84cc16', '#14b8a6',
];

export default function TimelineChart({ timeline }) {
    const { buckets, cluster_ids, cluster_labels } = timeline;
    const [hidden, setHidden] = useState(new Set());

    if (!buckets || buckets.length === 0) {
        return (
            <div className="panel fade-in">
                <div className="panel-header">
                    <span className="panel-title">TIMELINE — ERROR FREQUENCY OVER TIME</span>
                </div>
                <div style={{ padding: 24, color: 'var(--text-dim)', fontFamily: 'var(--font-mono)', fontSize: 12 }}>
                    No timestamp data available for timeline.
                </div>
            </div>
        );
    }

    function toggleCluster(cid) {
        setHidden(prev => {
            const next = new Set(prev);
            next.has(cid) ? next.delete(cid) : next.add(cid);
            return next;
        });
    }

    const CustomTooltip = ({ active, payload, label }) => {
        if (!active || !payload?.length) return null;
        return (
            <div className="tooltip-box">
                <div style={{ color: 'var(--text-dim)', marginBottom: 6 }}>{label}</div>
                {payload.map(p => (
                    p.value > 0 && (
                        <div key={p.dataKey} style={{ color: p.color, marginBottom: 2 }}>
                            {cluster_labels[p.dataKey.replace('cluster_', '')]} — {p.value} errors
                        </div>
                    )
                ))}
            </div>
        );
    };

    return (
        <div className="panel fade-in">
            <div className="panel-header">
                <span className="panel-title">TIMELINE — ERROR FREQUENCY OVER TIME</span>
                <div className="formats-list">
                    {cluster_ids.map((cid, i) => (
                        <div
                            key={cid}
                            className="format-badge"
                            onClick={() => toggleCluster(cid)}
                            style={{
                                cursor: 'pointer',
                                opacity: hidden.has(cid) ? 0.3 : 1,
                                transition: 'opacity 0.2s',
                            }}
                        >
                            <div style={{
                                width: 8, height: 8, borderRadius: '50%',
                                background: CLUSTER_COLORS[i % CLUSTER_COLORS.length],
                                flexShrink: 0,
                            }} />
                            <span className="format-name" style={{ color: 'var(--text-mid)' }}>
                                {cluster_labels[cid]}
                            </span>
                        </div>
                    ))}
                </div>
            </div>

            <div style={{ padding: '16px 8px 8px' }}>
                <ResponsiveContainer width="100%" height={300}>
                    <LineChart data={buckets} margin={{ top: 8, right: 24, bottom: 8, left: 0 }}>
                        <XAxis
                            dataKey="time"
                            tick={{ fill: 'var(--text-dim)', fontSize: 10, fontFamily: 'var(--font-mono)' }}
                            axisLine={{ stroke: 'var(--border)' }}
                            tickLine={false}
                            interval={Math.floor(buckets.length / 8)}
                        />
                        <YAxis
                            tick={{ fill: 'var(--text-dim)', fontSize: 10, fontFamily: 'var(--font-mono)' }}
                            axisLine={{ stroke: 'var(--border)' }}
                            tickLine={false}
                            allowDecimals={false}
                        />
                        <Tooltip content={<CustomTooltip />} />
                        {cluster_ids.map((cid, i) => (
                            !hidden.has(cid) && (
                                <Line
                                    key={cid}
                                    type="monotone"
                                    dataKey={`cluster_${cid}`}
                                    stroke={CLUSTER_COLORS[i % CLUSTER_COLORS.length]}
                                    strokeWidth={2}
                                    dot={false}
                                    activeDot={{ r: 4, strokeWidth: 0 }}
                                    isAnimationActive={true}
                                />
                            )
                        ))}
                    </LineChart>
                </ResponsiveContainer>
            </div>
        </div>
    );
}