/* global React */
const { useState, useMemo, useEffect, useRef } = React;
// =============== Icons (small inline SVGs) ===============
const Icon = ({name, size=16, stroke=1.7}) => {
const p = {width:size, height:size, viewBox:"0 0 24 24", fill:"none", stroke:"currentColor",
strokeWidth:stroke, strokeLinecap:"round", strokeLinejoin:"round"};
const g = {
grid:
,
map:
,
users:
,
user:,
rocket:
,
network:
,
trophy:
,
calendar:
,
bell:,
search:,
settings:,
arrowRight:,
arrowUp:,
plus:,
spark:,
chip:
,
layers:
,
building:
,
briefcase:,
book:,
target:,
flame:,
star:,
check:,
eye:,
msg:,
filter:,
};
return ;
};
window.Icon = Icon;
// =============== Avatar ===============
function Avatar({name, initials, color, size='md'}) {
const cls = size==='sm' ? 'avatar-sm' : size==='lg' ? 'avatar-lg' : size==='xl' ? 'avatar-xl' : '';
const colors = ['#0B5FBE','#0E3F84','#7C3AED','#DB2777','#0891B2','#059669','#F59E0B','#DC2626','#0EA5E9','#6366F1'];
const seed = (name||'?').charCodeAt(0) + (name||'?').charCodeAt(1 || 0) || 0;
const bg = color || colors[Math.abs(seed) % colors.length];
return
{initials}
;
}
function shade(hex, p) {
const n = hex.replace('#','');
const r = parseInt(n.slice(0,2),16), g = parseInt(n.slice(2,4),16), b = parseInt(n.slice(4,6),16);
const f = (c) => Math.max(0, Math.min(255, Math.round(c + 255*p)));
return '#'+[f(r),f(g),f(b)].map(c=>c.toString(16).padStart(2,'0')).join('');
}
window.Avatar = Avatar;
window.shade = shade;
// =============== Badge / Selo ===============
function Selo({id, small=false}) {
const s = window.SELOS[id]; if (!s) return null;
return
{s.icon}
{small ? null : s.nome}
;
}
window.Selo = Selo;
function ClassifPill({id, withDot=true}) {
const c = window.CLASSIFICACOES.find(x=>x.id===id); if (!c) return null;
return
{withDot && }
{c.nome}
;
}
window.ClassifPill = ClassifPill;
// =============== Sparkline ===============
function Sparkline({data, w=80, h=22, color='#0B5FBE', fill=true}) {
const max = Math.max(...data), min = Math.min(...data);
const range = max - min || 1;
const dx = w / (data.length - 1);
let path = '';
data.forEach((v, i) => {
const x = i * dx, y = h - ((v - min) / range) * (h - 2) - 1;
path += (i===0?'M':'L') + x.toFixed(1) + ' ' + y.toFixed(1) + ' ';
});
const fillPath = path + `L ${w} ${h} L 0 ${h} Z`;
return ;
}
window.Sparkline = Sparkline;
// =============== Radar Chart ===============
function Radar({dim, size=200, color='#0B5FBE'}) {
const cx = size/2, cy = size/2, R = size/2 - 22;
const labels = [
{key:'engajamento', name:'Engajamento'},
{key:'execucao', name:'Execução'},
{key:'colaboracao', name:'Colaboração'},
{key:'reconhecimento', name:'Reconhecimento'},
{key:'maturidade', name:'Maturidade'},
];
const N = labels.length;
const point = (val, i) => {
const a = -Math.PI/2 + i * 2*Math.PI/N;
const r = (val/100) * R;
return [cx + r*Math.cos(a), cy + r*Math.sin(a)];
};
const grid = [0.25, 0.5, 0.75, 1].map(f =>
labels.map((_, i) => point(100*f, i)).map(p=>p.join(',')).join(' ')
);
const dataPoly = labels.map((l, i) => point(dim[l.key], i)).map(p=>p.join(',')).join(' ');
return (
{labels.map(l => (
{l.name}
{dim[l.key]}
))}
);
}
window.Radar = Radar;
// =============== Trail (gamified progression) ===============
function Trail({points}) {
const steps = window.CLASSIFICACOES;
const currentIdx = steps.findIndex(s => points >= s.faixa[0] && points < s.faixa[1]);
return (
{steps.map((s, i) => {
const done = i < currentIdx;
const current = i === currentIdx;
const cls = done ? 'done' : current ? 'current' : '';
return (
{done ? '✓' : i+1}
{s.nome}
{s.faixa[0]}–{s.faixa[1]>=999?'∞':s.faixa[1]} pts
);
})}
);
}
window.Trail = Trail;
// =============== Bar list ===============
function BarRow({name, value, max, color='#0B5FBE'}) {
const pct = (value/max)*100;
return ;
}
window.BarRow = BarRow;
Object.assign(window, {Icon, Avatar, shade, Selo, ClassifPill, Sparkline, Radar, Trail, BarRow});