File size: 4,077 Bytes
382dea6
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
import React, { useState, useEffect, useRef } from 'react';
import { adminApi } from '../api/client';
import { Activity, Trash2, RefreshCw } from 'lucide-react';
import CacheStats from './CacheStats';
import LatencyChart from './LatencyChart';
import ClusterViz from './ClusterViz';

const AdminPanel = ({ stats, analytics, fetchData }) => {
  const [isEvaluating, setIsEvaluating] = useState(false);
  const [evalResults, setEvalResults] = useState(null);
  const [adminMessage, setAdminMessage] = useState('');

  const evalRef = useRef(null);

  const handleClearCache = async () => {
    try {
      await adminApi.clearCache();
      setAdminMessage('Cache cleared successfully!');
      setTimeout(() => setAdminMessage(''), 3000);
      fetchData();
    } catch (err) {
      console.error("Failed to clear cache", err);
    }
  };

  const handleRunEvaluation = async () => {
    setIsEvaluating(true);
    try {
      const res = await adminApi.runEvaluation();
      setEvalResults(res.data);
      setTimeout(() => {
        if (evalRef.current) {
          evalRef.current.scrollIntoView({ behavior: 'smooth' });
        }
      }, 100);
    } catch (err) {
      console.error("Eval failed", err);
    }
    setIsEvaluating(false);
  };

  return (
    <div className="admin-panel glass-panel" style={{ display: 'flex', flexDirection: 'column', height: '100%' }}>
      <div className="admin-header">
        <h3><Activity size={18}/> System Dashboard</h3>
        <button className="icon-button" onClick={fetchData}><RefreshCw size={14}/></button>
      </div>

      <div className="charts-grid" style={{ flex: 1, overflowY: 'auto', paddingRight: '0.5rem' }}>
        <CacheStats stats={stats} analytics={analytics} />
        <LatencyChart analytics={analytics} />
        <div style={{ gridColumn: '1 / -1' }}>
          <ClusterViz />
        </div>
        
        {evalResults && !evalResults.error && (
          <div style={{ gridColumn: '1 / -1' }} ref={evalRef}>
            <div className="eval-results glass-panel fade-in" style={{ marginTop: '1rem', overflowX: 'auto' }}>
              <h4>Retrieval Evaluation (NDCG/MRR)</h4>
              <table className="eval-table">
                <thead>
                  <tr>
                    <th>Mode</th>
                    <th>NDCG@10</th>
                    <th>MRR</th>
                    <th>P@3</th>
                    <th>R@10</th>
                    <th>MAP</th>
                  </tr>
                </thead>
                <tbody>
                  {Object.entries(evalResults).map(([mode, metrics]) => (
                    <tr key={mode}>
                      <td>{mode}</td>
                      <td>{metrics['ndcg@10'].toFixed(4)}</td>
                      <td>{metrics['mrr'].toFixed(4)}</td>
                      <td>{metrics['p@3'].toFixed(4)}</td>
                      <td>{metrics['r@10'].toFixed(4)}</td>
                      <td>{metrics['map'].toFixed(4)}</td>
                    </tr>
                  ))}
                </tbody>
              </table>
            </div>
          </div>
        )}
        {evalResults?.error && (
          <div style={{ gridColumn: '1 / -1' }}>
            <div className="eval-error">{evalResults.error}</div>
          </div>
        )}
      </div>

      <div className="admin-controls" style={{ marginTop: '1rem', borderTop: '1px solid rgba(255,255,255,0.1)', paddingTop: '1rem' }}>
        
        <div className="button-group" style={{marginTop: '1rem', display: 'flex', alignItems: 'center', gap: '1rem'}}>
          <button onClick={handleClearCache} className="btn-danger">
            <Trash2 size={14}/> Clear Cache
          </button>
          <button onClick={handleRunEvaluation} className="btn-primary" disabled={isEvaluating}>
            {isEvaluating ? 'Evaluating...' : 'Load IR Metrics'}
          </button>
          {adminMessage && <span style={{ color: 'var(--accent-primary)', fontSize: '0.8rem' }}>{adminMessage}</span>}
        </div>
      </div>
    </div>
  );
};

export default AdminPanel;