File size: 4,253 Bytes
7b4f5dd
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
/* ═══════════════════════════════════════════════════════════════
   FindingCard β€” Expandable finding card with severity + code
   ═══════════════════════════════════════════════════════════════ */

import { useState } from 'react';
import SeverityBadge from './SeverityBadge';
import './FindingCard.css';

const AGENT_ICONS = {
  security: 'πŸ”',
  performance: '⚑',
  fix: 'πŸ”§',
};

export default function FindingCard({ finding, index, fix }) {
  const [isExpanded, setIsExpanded] = useState(false);

  return (
    <div
      className={`finding-card glass-card-static animate-slide-in`}
      style={{ animationDelay: `${index * 0.05}s` }}
    >
      <div
        className="finding-card-header"
        onClick={() => setIsExpanded(!isExpanded)}
      >
        <div className="finding-header-left">
          <SeverityBadge severity={finding.severity} />
          <span className="finding-agent-icon" title={`${finding.agent} agent`}>
            {AGENT_ICONS[finding.agent] || 'πŸ”'}
          </span>
        </div>
        <div className="finding-header-right">
          {finding.cwe && <span className="tag">{finding.cwe}</span>}
          <button className="expand-btn" aria-label="Toggle details">
            {isExpanded ? 'β–Ύ' : 'β–Έ'}
          </button>
        </div>
      </div>

      <div className="finding-title-row">
        <h4 className="finding-title">{finding.title}</h4>
        {finding.id && <span className="finding-id mono">{finding.id}</span>}
      </div>

      <p className="finding-description">{finding.description}</p>

      {finding.file && (
        <div className="finding-location">
          <span className="location-icon">πŸ“„</span>
          <span className="location-path mono">{finding.file}</span>
          {finding.line && <span className="location-line mono">:{finding.line}</span>}
        </div>
      )}

      {/* Expanded Details */}
      {isExpanded && (
        <div className="finding-details animate-fade-in">
          {/* Code Snippet */}
          {finding.code && (
            <div className="finding-code-section">
              <div className="code-section-label">Vulnerable Code</div>
              <div className="code-block">
                <pre><code>{finding.code}</code></pre>
              </div>
            </div>
          )}

          {/* Suggestion */}
          {finding.suggestion && (
            <div className="finding-suggestion">
              <div className="code-section-label">πŸ’‘ Recommendation</div>
              <p>{finding.suggestion}</p>
            </div>
          )}

          {/* Fix Preview */}
          {fix && (
            <div className="finding-fix-preview">
              <div className="code-section-label">πŸ”§ AI-Generated Fix</div>
              <div className="diff-container">
                <div className="diff-panel diff-before">
                  <div className="diff-header">Before</div>
                  <div className="code-block">
                    <pre><code>{fix.before}</code></pre>
                  </div>
                </div>
                <div className="diff-arrow">β†’</div>
                <div className="diff-panel diff-after">
                  <div className="diff-header">After</div>
                  <div className="code-block">
                    <pre><code>{fix.after}</code></pre>
                  </div>
                </div>
              </div>
              {fix.explanation && (
                <p className="fix-explanation">{fix.explanation}</p>
              )}
            </div>
          )}
        </div>
      )}

      {/* Quick action bar */}
      <div className="finding-actions">
        {finding.fixAvailable && !fix && (
          <span className="fix-available-tag">
            <span>πŸ”§</span> Fix available
          </span>
        )}
        {fix && (
          <span className="fix-ready-tag">
            <span>βœ…</span> Fix generated
          </span>
        )}
      </div>
    </div>
  );
}