/* global React, ReactDOM */ const { useState: U, useEffect: E, useRef: R, useMemo: M } = React; const { Icon, DashboardView, TalentosView, ProjetosView, RedeView, OportunidadesView, MeuPerfilView, EmpresaView, TalentDetail, Avatar } = window; const PERSONAS = [ {id:'comite', name:'Comitê / Gestor regional', sub:'Visão executiva da rede', color:'#0B5FBE', icon:'briefcase'}, {id:'talento', name:'Maria — Talento', sub:'Estudante / profissional', color:'#06B6D4', icon:'user'}, {id:'empresa', name:'Iguatu Têxtil — Empresa', sub:'Buscando talentos', color:'#10B981', icon:'building'}, {id:'instituicao',name:'IFCE — Instituição', sub:'Educação / Sebrae', color:'#F59E0B', icon:'book'}, {id:'mentor', name:'Cristiane — Mentora', sub:'Mentoria & orientação', color:'#7C3AED', icon:'star'}, {id:'startup', name:'AgroSensor — Startup', sub:'Projeto em validação', color:'#DB2777', icon:'rocket'}, ]; const NAV_BY_PERSONA = { comite: [{k:'mapa',l:'Mapa',i:'map'},{k:'dashboard',l:'Visão geral',i:'grid'},{k:'talentos',l:'Talentos',i:'users',c:24},{k:'projetos',l:'Projetos',i:'rocket',c:14},{k:'oportunidades',l:'Oportunidades',i:'trophy',c:8},{k:'mentores',l:'Mentores',i:'star'}], talento: [{k:'mapa',l:'Mapa',i:'map'},{k:'meu',l:'Meu perfil',i:'user'},{k:'oportunidades',l:'Oportunidades',i:'trophy',c:8},{k:'projetos',l:'Projetos',i:'rocket'}], empresa: [{k:'mapa',l:'Mapa',i:'map'},{k:'empresa',l:'Buscar talentos',i:'users'},{k:'projetos',l:'Projetos',i:'rocket'}], instituicao:[{k:'mapa',l:'Mapa',i:'map'},{k:'dashboard',l:'Visão geral',i:'grid'},{k:'talentos',l:'Meus alunos',i:'users',c:24},{k:'oportunidades',l:'Editais',i:'trophy'}], mentor: [{k:'mapa',l:'Mapa',i:'map'},{k:'meu',l:'Meu perfil',i:'user'},{k:'talentos',l:'Mentorados',i:'users',c:14},{k:'projetos',l:'Projetos apoiados',i:'rocket'},{k:'oportunidades',l:'Oportunidades',i:'trophy'}], startup: [{k:'mapa',l:'Mapa',i:'map'},{k:'meu',l:'Meu projeto',i:'rocket'},{k:'oportunidades',l:'Editais & eventos',i:'trophy'},{k:'talentos',l:'Procurar time',i:'users'}], }; const TWEAK_DEFAULTS = /*EDITMODE-BEGIN*/{ "primaryColor":"#0B5FBE", "density":"normal", "dashboardLayout":"grid" }/*EDITMODE-END*/; function App() { const [persona, setPersona] = U('comite'); const [view, setView] = U('mapa'); const [personaOpen, setPersonaOpen] = U(false); const [talentoSel, setTalentoSel] = U(null); const [search, setSearch] = U(''); const [searchOpen, setSearchOpen] = U(false); const tweaks = (window.useTweaks ? window.useTweaks(TWEAK_DEFAULTS) : [TWEAK_DEFAULTS, ()=>{}]); const t = tweaks[0]; const setT = tweaks[1]; E(() => { document.documentElement.style.setProperty('--primary', t.primaryColor); document.documentElement.style.setProperty('--blue-700', t.primaryColor); document.documentElement.dataset.density = t.density; document.documentElement.dataset.dashboard = t.dashboardLayout; }, [t.primaryColor, t.density, t.dashboardLayout]); E(() => { const nav = NAV_BY_PERSONA[persona]; if (!nav.find(n => n.k === view)) setView(nav[0].k); }, [persona]); // Close persona panel on click outside E(() => { if (!personaOpen) return; const onClick = (e) => { if (!e.target.closest('.persona-panel') && !e.target.closest('.persona')) setPersonaOpen(false); }; document.addEventListener('click', onClick); return () => document.removeEventListener('click', onClick); }, [personaOpen]); const onSelectTalento = (id) => setTalentoSel(id); const onSelectProjeto = () => setView('projetos'); const persObj = PERSONAS.find(p => p.id === persona); const renderView = () => { switch(view) { case 'mapa': return ; case 'dashboard': return ; case 'talentos': return ; case 'projetos': return ; case 'oportunidades': return ; case 'mentores': return ; case 'meu': return ; case 'empresa': return ; default: return ; } }; const nav = NAV_BY_PERSONA[persona]; return
Inova Centro-Sul
setSearch(e.target.value)} onBlur={()=>{ if(!search) setSearchOpen(false); }}/>
{ e.stopPropagation(); setPersonaOpen(o=>!o); }}> {persObj.name.split('—')[0].trim()}
{personaOpen && (
Visualizar plataforma como…
{PERSONAS.map(p => (
{ setPersona(p.id); setPersonaOpen(false); }}>
{p.name}
{p.sub}
))}
)}
{renderView()}
{talentoSel && setTalentoSel(null)} onOpenProjeto={() => { setTalentoSel(null); setView('projetos'); }}/>} {window.TweaksPanel && (
{PERSONAS.map(p => ( ))}
)}
; } // ============ Mapa standalone page — fullbleed map + click-to-open city popover ============ function MapaPage({onSelectTalento}) { const [heatmap, setHeatmap] = U('talentos'); const [focused, setFocused] = U(null); const [tab, setTab] = U('talentos'); const [filter, setFilter] = U('todos'); const stageRef = R(null); const [allOpen, setAllOpen] = U(false); const [zoomOut, setZoomOut] = U(0); // 0..1 accumulator for "afastar" gesture const zoomTimer = R(null); const focMun = focused ? window.MUNICIPALITIES.find(c => c.id === focused) : null; // Detectar gesto de "afastar" o mapa: roda do mouse para cima (deltaY > 0) ou pinch out. // Acumula um valor 0..1; ao passar 1, abre a tela com todos os talentos. E(() => { const el = stageRef.current; if (!el) return; const onWheel = (e) => { if (allOpen || focMun) return; // deltaY positivo = afastando (scroll para baixo / pinch para fora em trackpad é geralmente negativo; // priorizamos o scroll-wheel para cima = zoom-out clássico) if (e.deltaY > 0) { e.preventDefault(); setZoomOut(z => { const next = Math.min(1, z + Math.min(0.06, e.deltaY / 600)); if (next >= 1) { setAllOpen(true); return 0; } return next; }); clearTimeout(zoomTimer.current); zoomTimer.current = setTimeout(() => setZoomOut(0), 700); } }; el.addEventListener('wheel', onWheel, {passive:false}); return () => el.removeEventListener('wheel', onWheel); }, [allOpen, focMun]); const talentosCity = M(() => focMun ? window.TALENTOS.filter(t => t.cidade === focMun.nome) : [], [focMun]); const projetosCity = M(() => focMun ? window.PROJETOS_INOV.filter(p => p.cidade === focMun.nome) : [], [focMun]); const oportunidadesCity = M(() => focMun ? (window.OPORTUNIDADES||[]).filter(o => !o.cidade || o.cidade === focMun.nome || o.cidade === 'Centro-Sul') : [], [focMun]); const mentoresCity = M(() => focMun ? (window.MENTORES||[]).filter(m => m.cidade === focMun.nome) : [], [focMun]); // Filters const PROJ_FILTERS = ['todos','ideação','validação','MVP','operação']; // Talentos: sempre ordenados por pontuação na trilha (decrescente) const filteredTalents = M(() => { return [...talentosCity].sort((a,b) => (b.pontos||0) - (a.pontos||0)); }, [talentosCity]); const filteredProjs = M(() => { if (filter === 'todos') return projetosCity; return projetosCity.filter(p => (p.fase||'').toLowerCase().includes(filter.toLowerCase())); }, [projetosCity, filter]); E(() => { setFilter('todos'); }, [tab, focMun]); // Compute popover position next to clicked city const [popPos, setPopPos] = U({left: 20, top: 80}); E(() => { if (!focMun || !stageRef.current) return; const stage = stageRef.current.getBoundingClientRect(); const svg = stageRef.current.querySelector('svg.map-svg'); if (!svg) return; // Find the path for this municipality const pathEl = svg.querySelector(`path[data-mun="${focMun.id}"]`); let cx, cy; if (pathEl) { const bb = pathEl.getBoundingClientRect(); cx = bb.left + bb.width/2 - stage.left; cy = bb.top + bb.height/2 - stage.top; } else { cx = stage.width/2; cy = stage.height/2; } // Place to the right by default; flip if not enough space const popW = 420, popH = 480; let left = cx + 30; if (left + popW > stage.width - 12) left = cx - popW - 30; if (left < 12) left = 12; let top = cy - popH/2; if (top < 12) top = 12; if (top + popH > stage.height - 12) top = stage.height - popH - 12; setPopPos({ left, top }); }, [focMun]); return
{/* Hint de "afastar para ver todos" */} {zoomOut > 0.05 && !allOpen && (
Continue afastando
Ver todos os talentos da região
)} {/* top-left toolbar — heatmap modes */}
{[{k:'talentos',l:'Talentos'},{k:'projetos',l:'Projetos'}, {k:'eventos',l:'Eventos'},{k:'mr',l:'Microrregiões'}].map(t => (
setHeatmap(t.k)}>{t.l}
))}
{/* top-right info panel when nothing focused */} {!focMun && (
Mapa Regional de Talentos
16 municípios · 5 microrregiões
Clique em um município para explorar talentos, projetos, mentores e oportunidades. Use a barra superior para alternar entre modos de calor.
{window.TALENTOS.length}
Talentos
{window.PROJETOS_INOV.length}
Projetos
{(window.MENTORES||[]).length}
Mentores
{(window.OPORTUNIDADES||[]).length}
Oportunidades
)} {/* legend */}
{({talentos:'Densidade de talentos',projetos:'Densidade de projetos', eventos:'Eventos realizados',mr:'Microrregiões'})[heatmap]}
{heatmap === 'mr' ? Object.entries(window.MR_NAMES).map(([k,n]) => (
{n}
)) : <>
MenorMaior
}
{/* CITY POPOVER — segue o filtro superior do mapa */} {focMun && setFocused(null)}/>} {/* Botão flutuante: ver todos os talentos */} {!focMun && !allOpen && ( )} {allOpen && setAllOpen(false)} onSelectTalento={onSelectTalento}/>}
; } // ============ City popover — segue o heatmap superior ============ function CityPopover({mun, pos, heatmap, filter, setFilter, talentos, projetos, oportunidades, mentores, projFilters, onSelectTalento, onClose}) { const counts = { talentos: window.TALENTOS.filter(t=>t.cidade===mun.nome).length, projetos: window.PROJETOS_INOV.filter(p=>p.cidade===mun.nome).length, mentores: (window.MENTORES||[]).filter(m=>m.cidade===mun.nome).length, oportunidades: oportunidades.length, }; const mrColor = window.MR_COLORS[mun.mr]; // Map heatmap mode -> what to show const MODE_LABEL = {talentos:'Talentos', projetos:'Projetos', eventos:'Oportunidades & eventos', mr:'Resumo do município'}; const mode = heatmap; // 'talentos' | 'projetos' | 'eventos' | 'mr' const filters = mode==='projetos' ? projFilters : null; return
e.stopPropagation()}>
{mun.mr} {window.MR_NAMES[mun.mr]} {mun.isPolo && POLO REGIONAL}

{mun.nome}

{(mun.pop/1000).toFixed(1)}k habitantes · IDH {mun.idh.toFixed(2)} · {mun.empresas} empresas · {mun.setor}
{/* Header da seção atual — reflete o filtro superior do mapa */}
{MODE_LABEL[mode]}
{mode==='talentos' && counts.talentos} {mode==='projetos' && counts.projetos} {mode==='eventos' && counts.oportunidades} {mode==='mr' && '—'}
{filters && (
{filters.map(f => (
setFilter(f)} style={{textTransform:'capitalize'}}> {f}
))}
)}
{mode==='talentos' && (talentos.length === 0 ?
Nenhum talento cadastrado.
: talentos.map((t, idx) => (
onSelectTalento(t.id)}>
{idx+1}
{t.nome}
{t.curso}
{t.pontos}
pontos
)))} {mode==='projetos' && (projetos.length === 0 ?
Nenhum projeto neste município{filter!=='todos'?' com este filtro':''}.
: projetos.map(p => (
{p.nome}
{p.fase} · {p.area}
{p.matchScore &&
{p.matchScore}%
}
{p.descricao &&
{p.descricao}
}
)))} {mode==='eventos' && (oportunidades.length === 0 ?
Nenhuma oportunidade ativa para este município.
: oportunidades.map(o => (
{o.tipo}
{o.titulo}
{o.prazo &&
{o.prazo}
}
{o.org}
)))} {mode==='mr' && (
{counts.talentos}
Talentos
{counts.projetos}
Projetos
{counts.mentores}
Mentores
{counts.oportunidades}
Edital/eventos
{mun.nome} integra a {window.MR_NAMES[mun.mr]}. Setor predominante: {mun.setor}. Para explorar talentos, projetos ou oportunidades, troque o filtro superior do mapa.
)}
; } const CLASS_COLOR = { 'Iniciante':'#94A3B8','Intermediário':'#0E7490','Avançado':'#0B5FBE','Expert':'#7C3AED' }; // ============ MENTORES ============ function MentoresView() { return

Mentores

{window.MENTORES.length} mentores ativos · {window.CONEXOES.filter(c=>c.tipo==='mentora').length} mentorias em andamento
{window.MENTORES.map(m => (
{m.nome}
{m.org} · {m.cidade}
{m.foco.map(f => {f})}

{m.bio}

{m.mentorados} mentorados {m.slots} vagas abertas
))}
; } window.MentoresView = MentoresView; // ============ All Talentos Overlay — abre ao "afastar" o mapa ============ function AllTalentosOverlay({onClose, onSelectTalento}) { const [q, setQ] = U(''); const [mr, setMr] = U('todas'); const [sort, setSort] = U('pontos'); const list = M(() => { let arr = [...window.TALENTOS]; if (mr !== 'todas') { const cidadesMR = window.MUNICIPALITIES.filter(m => m.mr === mr).map(m => m.nome); arr = arr.filter(t => cidadesMR.includes(t.cidade)); } if (q.trim()) { const s = q.toLowerCase(); arr = arr.filter(t => t.nome.toLowerCase().includes(s) || (t.curso||'').toLowerCase().includes(s) || (t.cidade||'').toLowerCase().includes(s) || (t.skills||[]).some(k => k.toLowerCase().includes(s))); } if (sort === 'pontos') arr.sort((a,b) => (b.pontos||0)-(a.pontos||0)); else if (sort === 'nome') arr.sort((a,b) => a.nome.localeCompare(b.nome)); else if (sort === 'cidade') arr.sort((a,b) => a.cidade.localeCompare(b.cidade)); return arr; }, [q, mr, sort]); E(() => { const onKey = (e) => { if (e.key === 'Escape') onClose(); }; window.addEventListener('keydown', onKey); return () => window.removeEventListener('keydown', onKey); }, []); return
e.stopPropagation()} style={{flex:1, display:'flex', flexDirection:'column', margin:'40px auto', maxWidth:1280, width:'calc(100% - 64px)', background:'#fff', borderRadius:18, boxShadow:'0 28px 80px rgba(11,18,32,.4)', overflow:'hidden', animation:'popIn .25s cubic-bezier(.2,.8,.3,1.1)'}}>
Visão regional

Todos os talentos · {list.length} de {window.TALENTOS.length}

setQ(e.target.value)} placeholder="Buscar por nome, curso, skill ou cidade…" style={{width:'100%', height:42, borderRadius:10, border:'1px solid #E2E8F0', padding:'0 14px 0 38px', fontSize:13, outline:'none', background:'#F8FAFC'}}/>
Microrregião: {[['todas','Todas'], ...Object.entries(window.MR_NAMES)].map(([k,l]) => ( ))}
Ordenar: {[['pontos','Pontos'],['nome','Nome'],['cidade','Cidade']].map(([k,l]) => ( ))}
{list.map(t => (
{ onSelectTalento(t.id); onClose(); }} style={{background:'#fff', borderRadius:12, padding:16, border:'1px solid #E2E8F0', cursor:'pointer', transition:'transform .12s, box-shadow .12s', display:'flex', flexDirection:'column', gap:10}} onMouseEnter={e=>{e.currentTarget.style.transform='translateY(-2px)'; e.currentTarget.style.boxShadow='0 10px 24px rgba(11,18,32,.08)';}} onMouseLeave={e=>{e.currentTarget.style.transform=''; e.currentTarget.style.boxShadow='';}}>
{t.nome}
{t.curso}
{t.pontos}
pts
{(t.skills||[]).slice(0,3).map(s => {s})}
{t.cidade} {t.classificacao}
))} {list.length === 0 && (
Nenhum talento encontrado.
)}
; } ReactDOM.createRoot(document.getElementById('app')).render();