larxius commited on
Commit
07a5fa4
·
verified ·
1 Parent(s): 34a7a1e

Update frontend/src/components/ThreatGauge.jsx

Browse files
Files changed (1) hide show
  1. frontend/src/components/ThreatGauge.jsx +101 -103
frontend/src/components/ThreatGauge.jsx CHANGED
@@ -1,103 +1,101 @@
1
- import React from 'react';
2
-
3
- export const ThreatGauge = ({ score }) => {
4
- // Score styling color bounds
5
- let color = 'var(--low)';
6
- let label = 'SECURE';
7
-
8
- if (score < 40) {
9
- color = 'var(--critical)';
10
- label = 'CRITICAL RISK';
11
- } else if (score < 60) {
12
- color = 'var(--high)';
13
- label = 'HIGH RISK';
14
- } else if (score < 80) {
15
- color = 'var(--medium)';
16
- label = 'MEDIUM WARNING';
17
- }
18
-
19
- // Radial calculation (semi circle, SVG path)
20
- const radius = 80;
21
- const strokeWidth = 10;
22
- const normalizedScore = Math.min(100, Math.max(0, score || 0));
23
-
24
- // Circumference for a full circle is 2 * pi * r. For semi-circle it's half.
25
- // We use strokeDasharray to fill the arc.
26
- const circumference = radius * Math.PI;
27
- const strokeDashoffset = circumference - (normalizedScore / 100) * circumference;
28
-
29
- return (
30
- <div style={{
31
- display: 'flex',
32
- flexDirection: 'column',
33
- alignItems: 'center',
34
- justifyContent: 'center',
35
- padding: 'var(--space-md)'
36
- }}>
37
- <div style={{ position: 'relative', width: '200px', height: '110px' }}>
38
- <svg width="200" height="110" style={{ transform: 'rotate(180deg)' }}>
39
- {/* Base Gauge Arch (Unfilled) */}
40
- <circle
41
- cx="100"
42
- cy="10"
43
- r={radius}
44
- fill="none"
45
- stroke="var(--outline-variant)"
46
- strokeWidth={strokeWidth}
47
- strokeDasharray={circumference}
48
- strokeLinecap="round"
49
- style={{ transform: 'translate(0, 100px)' }}
50
- />
51
- {/* Filled Gauge Arch */}
52
- <circle
53
- cx="100"
54
- cy="10"
55
- r={radius}
56
- fill="none"
57
- stroke={color}
58
- strokeWidth={strokeWidth}
59
- strokeDasharray={circumference}
60
- strokeDashoffset={strokeDashoffset}
61
- strokeLinecap="round"
62
- style={{
63
- transform: 'translate(0, 100px)',
64
- transition: 'stroke-dashoffset 1s cubic-bezier(0.4, 0, 0.2, 1), stroke 0.5s ease'
65
- }}
66
- />
67
- </svg>
68
-
69
- {/* Score & Label inside circle */}
70
- <div style={{
71
- position: 'absolute',
72
- bottom: '0',
73
- left: '0',
74
- right: '0',
75
- display: 'flex',
76
- flexDirection: 'column',
77
- alignItems: 'center',
78
- justifyContent: 'center'
79
- }}>
80
- <span style={{
81
- fontSize: '36px',
82
- fontWeight: 700,
83
- letterSpacing: '-0.02em',
84
- color: 'var(--on-surface)',
85
- lineHeight: '1.0'
86
- }}>
87
- {score !== null && score !== undefined ? score : '--'}
88
- </span>
89
- <span style={{
90
- fontSize: '11px',
91
- fontFamily: 'JetBrains Mono',
92
- fontWeight: 600,
93
- color: color,
94
- marginTop: '4px',
95
- letterSpacing: '0.05em'
96
- }}>
97
- {label}
98
- </span>
99
- </div>
100
- </div>
101
- </div>
102
- );
103
- };
 
1
+ export const ThreatGauge = ({ score }) => {
2
+ // Score styling color bounds
3
+ let color = 'var(--low)';
4
+ let label = 'SECURE';
5
+
6
+ if (score < 40) {
7
+ color = 'var(--critical)';
8
+ label = 'CRITICAL RISK';
9
+ } else if (score < 60) {
10
+ color = 'var(--high)';
11
+ label = 'HIGH RISK';
12
+ } else if (score < 80) {
13
+ color = 'var(--medium)';
14
+ label = 'MEDIUM WARNING';
15
+ }
16
+
17
+ // Radial calculation (semi circle, SVG path)
18
+ const radius = 80;
19
+ const strokeWidth = 10;
20
+ const normalizedScore = Math.min(100, Math.max(0, score || 0));
21
+
22
+ // Circumference for a full circle is 2 * pi * r. For semi-circle it's half.
23
+ // We use strokeDasharray to fill the arc.
24
+ const circumference = radius * Math.PI;
25
+ const strokeDashoffset = circumference - (normalizedScore / 100) * circumference;
26
+
27
+ return (
28
+ <div style={{
29
+ display: 'flex',
30
+ flexDirection: 'column',
31
+ alignItems: 'center',
32
+ justifyContent: 'center',
33
+ padding: 'var(--space-md)'
34
+ }}>
35
+ <div style={{ position: 'relative', width: '200px', height: '110px' }}>
36
+ <svg width="200" height="110" style={{ transform: 'rotate(180deg)' }}>
37
+ {/* Base Gauge Arch (Unfilled) */}
38
+ <circle
39
+ cx="100"
40
+ cy="10"
41
+ r={radius}
42
+ fill="none"
43
+ stroke="var(--outline-variant)"
44
+ strokeWidth={strokeWidth}
45
+ strokeDasharray={circumference}
46
+ strokeLinecap="round"
47
+ style={{ transform: 'translate(0, 100px)' }}
48
+ />
49
+ {/* Filled Gauge Arch */}
50
+ <circle
51
+ cx="100"
52
+ cy="10"
53
+ r={radius}
54
+ fill="none"
55
+ stroke={color}
56
+ strokeWidth={strokeWidth}
57
+ strokeDasharray={circumference}
58
+ strokeDashoffset={strokeDashoffset}
59
+ strokeLinecap="round"
60
+ style={{
61
+ transform: 'translate(0, 100px)',
62
+ transition: 'stroke-dashoffset 1s cubic-bezier(0.4, 0, 0.2, 1), stroke 0.5s ease'
63
+ }}
64
+ />
65
+ </svg>
66
+
67
+ {/* Score & Label inside circle */}
68
+ <div style={{
69
+ position: 'absolute',
70
+ bottom: '0',
71
+ left: '0',
72
+ right: '0',
73
+ display: 'flex',
74
+ flexDirection: 'column',
75
+ alignItems: 'center',
76
+ justifyContent: 'center'
77
+ }}>
78
+ <span style={{
79
+ fontSize: '36px',
80
+ fontWeight: 700,
81
+ letterSpacing: '-0.02em',
82
+ color: 'var(--on-surface)',
83
+ lineHeight: '1.0'
84
+ }}>
85
+ {score !== null && score !== undefined ? score : '--'}
86
+ </span>
87
+ <span style={{
88
+ fontSize: '11px',
89
+ fontFamily: 'JetBrains Mono',
90
+ fontWeight: 600,
91
+ color: color,
92
+ marginTop: '4px',
93
+ letterSpacing: '0.05em'
94
+ }}>
95
+ {label}
96
+ </span>
97
+ </div>
98
+ </div>
99
+ </div>
100
+ );
101
+ };