diff --git a/src/App.jsx b/src/App.jsx index 04763ed..a124cb6 100644 --- a/src/App.jsx +++ b/src/App.jsx @@ -26,27 +26,22 @@ function polar(angleDeg, r, cx, cy) { } function hexToRgb(hex) { - const r = parseInt(hex.slice(1,3),16); - const g = parseInt(hex.slice(3,5),16); - const b = parseInt(hex.slice(5,7),16); - return `${r},${g},${b}`; + return `${parseInt(hex.slice(1,3),16)},${parseInt(hex.slice(3,5),16)},${parseInt(hex.slice(5,7),16)}`; } function dominant(g) { return CATS.reduce((a,b) => DATA[g][a] >= DATA[g][b] ? a : b); } -function RadarChart({ highlighted, onSelect }) { - const S = 360, cx = 180, cy = 180, maxR = 130; +function RadarChart({ highlighted, onSelect, size }) { + const cx = size/2, cy = size/2, maxR = size/2 - 44; const step = 360 / CATS.length; - const rings = [1,2,3]; return ( - + {GROUPS.map(g => { - const dom = dominant(g); - const c = COLORS[dom]; + const c = COLORS[dominant(g)]; return ( @@ -56,35 +51,30 @@ function RadarChart({ highlighted, onSelect }) { })} - {/* rings */} - {rings.map(r => { + {[1,2,3].map(r => { const pts = CATS.map((_,i) => polar(i*step, (r/MAX_VAL)*maxR, cx, cy)); const d = pts.map((p,i) => `${i===0?'M':'L'}${p[0].toFixed(1)},${p[1].toFixed(1)}`).join(' ') + ' Z'; - return ; + return ; })} - {/* axes */} {CATS.map((cat,i) => { const [x,y] = polar(i*step, maxR, cx, cy); return ; })} - {/* group polygons */} {GROUPS.map(g => { const isHl = highlighted === g; const isOther = highlighted && !isHl; - const dom = dominant(g); - const col = COLORS[dom]; + const col = COLORS[dominant(g)]; const pts = CATS.map((cat,i) => { const r = DATA[g][cat] === 0 ? 4 : (DATA[g][cat]/MAX_VAL)*maxR; return polar(i*step, r, cx, cy); }); const d = pts.map((p,i) => `${i===0?'M':'L'}${p[0].toFixed(1)},${p[1].toFixed(1)}`).join(' ') + ' Z'; - const gId = g.replace('№','n'); return ( onSelect(isHl ? null : g)} style={{cursor:'pointer'}}> ( + style={{filter:`drop-shadow(0 0 6px ${COLORS[CATS[i]]})`}}/> ))} ); })} - {/* axis labels */} {CATS.map((cat,i) => { - const [x,y] = polar(i*step, maxR+26, cx, cy); + const [x,y] = polar(i*step, maxR+20, cx, cy); return ( {LABELS[cat]} ); })} - {/* ring numbers */} - {rings.map(r => { + {[1,2,3].map(r => { const [x,y] = polar(0, (r/MAX_VAL)*maxR, cx, cy); - return {r}; + return {r}; })} - {/* center dot */} ); @@ -127,15 +113,14 @@ function RadarChart({ highlighted, onSelect }) { function StatBar({ g }) { const total = CATS.reduce((s,c) => s+DATA[g][c], 0); - if (total === 0) return
нет активности
; + if (total === 0) return
нет активности
; return ( -
+
{CATS.map(cat => { const v = DATA[g][cat]; - const pct = total > 0 ? (v/MAX_VAL)*100 : 0; return (
-
{LABELS[cat]}
+
{LABELS[cat]}
@@ -151,87 +136,101 @@ function StatBar({ g }) { export default function App() { const [highlighted, setHighlighted] = useState(null); const [mounted, setMounted] = useState(false); + const [width, setWidth] = useState(typeof window !== 'undefined' ? window.innerWidth : 800); useEffect(() => { + const onResize = () => setWidth(window.innerWidth); + window.addEventListener('resize', onResize); setTimeout(() => setMounted(true), 100); + return () => window.removeEventListener('resize', onResize); }, []); - const totals = CATS.map(cat => ({ - cat, val: GROUPS.reduce((s,g) => s+DATA[g][cat], 0) - })); - const maxTotal = Math.max(...totals.map(t=>t.val)); + const isMobile = width < 640; + const radarSize = isMobile ? Math.min(width - 64, 290) : 320; - const hlData = highlighted ? DATA[highlighted] : null; - const hlTotal = hlData ? CATS.reduce((s,c)=>s+hlData[c],0) : 0; + const totals = CATS.map(cat => ({ cat, val: GROUPS.reduce((s,g) => s+DATA[g][cat], 0) })); + const maxTotal = Math.max(...totals.map(t=>t.val)); + const hlTotal = highlighted ? CATS.reduce((s,c)=>s+DATA[highlighted][c],0) : 0; return (
{/* Header */} -
-
+
+
Групповой анализ
-

+ WebkitBackgroundClip:'text', WebkitTextFillColor:'transparent', letterSpacing:1}}> РОЗА АКТИВНОСТИ

-
+
№1 – №11 · 4 НАПРАВЛЕНИЯ
{/* Legend */} -
+
{CATS.map(cat => ( -
-
- {LABELS[cat]} +
+
+ {LABELS[cat]}
))}
-
+ {/* Layout */} +
- {/* Radar */} + {/* Radar card */}
- + - {/* Highlighted info */} + {/* Detail panel */}
{highlighted ? ( <>
- {highlighted} - сумма: {hlTotal} + {highlighted} + сумма: {hlTotal}
) : ( -
+
Нажми на группу для деталей
)} @@ -239,46 +238,49 @@ export default function App() {
{/* Right panel */} -
+
- {/* Group list */} -
+ {/* Group chips */} +
Группы
- {GROUPS.map((g,idx) => { + {GROUPS.map(g => { const isHl = highlighted===g; - const dom = dominant(g); - const col = COLORS[dom]; + const col = COLORS[dominant(g)]; const total = CATS.reduce((s,c)=>s+DATA[g][c],0); const isEmpty = total === 0; return ( -
setHighlighted(isHl?null:g)} - style={{ - display:'flex',alignItems:'center',gap:8, - padding:'7px 12px',borderRadius:10,cursor:'pointer', - background: isHl ? `rgba(${hexToRgb(col)},0.18)` : 'rgba(255,255,255,0.03)', - border:`1px solid ${isHl ? col : 'rgba(255,255,255,0.07)'}`, - opacity: highlighted&&!isHl ? 0.4 : isEmpty ? 0.4 : 1, - transition:'all 0.2s', - boxShadow: isHl ? `0 0 16px ${col}40` : 'none', - minWidth:70, - }}> +
setHighlighted(isHl?null:g)} style={{ + display:'flex', alignItems:'center', gap:7, + padding: isMobile ? '10px 14px' : '7px 12px', + borderRadius:10, cursor:'pointer', + background: isHl ? `rgba(${hexToRgb(col)},0.18)` : 'rgba(255,255,255,0.04)', + border:`1px solid ${isHl ? col : 'rgba(255,255,255,0.08)'}`, + opacity: highlighted&&!isHl ? 0.4 : isEmpty ? 0.4 : 1, + transition:'all 0.2s', + boxShadow: isHl ? `0 0 14px ${col}40` : 'none', + flex: isMobile ? '1 0 calc(33% - 5px)' : 'unset', + minWidth: isMobile ? 'calc(33% - 5px)' : 68, + }}>
- {g} - {total} + boxShadow: isHl ? `0 0 8px ${col}` : 'none', flexShrink:0}}/> + {g} + {total}
); })}
- {/* Category totals */} -
Итого по направлениям @@ -287,8 +289,8 @@ export default function App() {
- {LABELS[cat]} -
+ {LABELS[cat]} +
@@ -298,33 +300,38 @@ export default function App() { ))}
- {/* Heat summary */} -
Тепловая карта
-
+
{CATS.map(c => ( -
{c.slice(0,3).toUpperCase()}
+
+ {c.slice(0,3).toUpperCase()} +
))} {GROUPS.map(g => ( <> -
{g}
+
{g}
{CATS.map(cat => { const v = DATA[g][cat]; - const alpha = v/MAX_VAL; return ( -
setHighlighted(highlighted===g?null:g)} - style={{ - height:20,borderRadius:4,cursor:'pointer', - background: v>0 ? `rgba(${hexToRgb(COLORS[cat])},${0.15+alpha*0.75})` : 'rgba(255,255,255,0.04)', - border: highlighted===g ? `1px solid ${COLORS[cat]}80` : '1px solid transparent', - display:'flex',alignItems:'center',justifyContent:'center', - fontSize:10,color:v>0?'rgba(255,255,255,0.9)':'rgba(255,255,255,0.15)', - fontWeight:700,transition:'all 0.2s', - }}> +
setHighlighted(highlighted===g?null:g)} style={{ + height: isMobile ? 28 : 22, borderRadius:4, cursor:'pointer', + background: v>0 ? `rgba(${hexToRgb(COLORS[cat])},${0.15+(v/MAX_VAL)*0.75})` : 'rgba(255,255,255,0.04)', + border: highlighted===g ? `1px solid ${COLORS[cat]}80` : '1px solid transparent', + display:'flex', alignItems:'center', justifyContent:'center', + fontSize: isMobile ? 12 : 10, + color: v>0 ? 'rgba(255,255,255,0.9)' : 'rgba(255,255,255,0.15)', + fontWeight:700, transition:'all 0.2s', + }}> {v||'·'}
); @@ -336,7 +343,7 @@ export default function App() {
-
+
НАЖМИ НА ГРУППУ · ВЫДЕЛИ НА РАДАРЕ