// Telas 2 e 3 — Inventário de dispositivos + Lista branca. const DSd = window.WiFi_eeb21e; const { Card: CardD, CardContent: CardContentD, CardHeader: CardHeaderD, CardTitle: CardTitleD, Badge: BadgeD, Button: BtnD, Input: InputD, Label: LabelD, Alert: AlertD, AlertTitle: AlertTitleD, AlertDescription: AlertDescD } = DSd; const { GTh: ThD, GTd: TdD, ChipFiltro: ChipD, BtnMini: BtnMiniD, TermoDica: TermoD, SelectG: SelD, mkLinha: mkD } = window; const FILTROS_INV = [ { id: 'todos', rot: 'Todos', f: () => true }, { id: 'com', rot: 'Comunicando', f: d => d.conexoes > 0 }, { id: 'lib', rot: 'Liberados', f: d => d.liberado }, { id: 'iot', rot: 'IoT', f: d => /IoT/.test(d.veredito) }, { id: 'nav', rot: 'Com navegador', f: d => /NAVEGADOR/.test(d.veredito) }, { id: 'rep', rot: 'Repetidores', f: d => /REPETIDOR/.test(d.veredito) }]; function TelaInventario({ site, sitesSel, openCmd }) { const meta = window.GESTAO_META; const list = sitesSel && sitesSel.length ? sitesSel : [site]; const multi = list.length > 1; const carga = window.useCarrega(() => Promise.all(list.map(k => window.GestaoAPI.inventario(k) .then(r => (r.dispositivos || []).map(d => ({ ...d, __k: k, __rot: meta[k].label }))))) .then(a => a.flat()), [list.join(',')]); const [filtro, setFiltro] = React.useState('todos'); const hoje = new Date().toISOString().slice(0, 10); const liberar = d => { const s = meta[d.__k]; openCmd({ titulo: 'Liberar MAC na lista branca', alvo: s.alvo, slug: d.__k, acao: 'binding-add', onSucesso: () => carga.recarrega(), mac: d.mac, comment: `CHARLES ${(d.hostname || d.vendor).toUpperCase()} ${hoje}`, linhas: [mkD(s.alvo, `/ip hotspot ip-binding add mac-address=${d.mac} type=bypassed server=${s.server} comment="CHARLES ${(d.hostname || d.vendor).toUpperCase()} ${hoje}"`)], nota: 'O vínculo pertence ao server, não ao profile — sobrevive a qualquer troca de portal. Comentário sempre preenchido: é o que identifica o aparelho daqui a um ano.' }); }; const remover = d => { const s = meta[d.__k]; openCmd({ titulo: 'Remover MAC da lista branca', alvo: s.alvo, slug: d.__k, acao: 'binding-del', mac: d.mac, onSucesso: () => carga.recarrega(), linhas: [mkD(s.alvo, `/ip hotspot ip-binding remove [find where mac-address=${d.mac}]`), mkD(s.alvo, '/ip hotspot ip-binding print where type=bypassed')], nota: 'A segunda linha confere o resultado. O aparelho volta a cair no portal na próxima conexão.' }); }; const base = carga.dados || []; const rows = base.filter(FILTROS_INV.find(x => x.id === filtro).f); return
{FILTROS_INV.map(f => setFiltro(f.id)}>{f.rot})}
{`${base.length} MACs · ${base.filter(d => d.conexoes > 0).length} comunicando · ${base.filter(d => d.liberado).length} liberados`}
{multi ? Ponto : null}MACFabricanteSSIDIPConexõesLiberadoVereditoAção {rows.map((d, i) => {multi ? {d.__rot} : null} {d.mac}{d.aleatorio ?
aleatório
: null}
{d.vendor}{d.hostname ?
{d.hostname}
: null}
{d.ssid}{d.ip}{d.conexoes || '—'}{d.liberado ? {d.comentario || 'sim'} : não}
{d.veredito}{d.sinais.length ?
{d.sinais.join(' · ')}
: null}
{d.liberado ? remover(d)}>Remover : liberar(d)}>Liberar})}
Veredito do inventário por tráfego: quem emite sonda de portal tem navegador; nuvem de fabricante indica IoT; OUI de equipamento de rede com tráfego de celular indica repetidor com NAT.
; } function macValido(m) { return /^([0-9A-Fa-f]{2}:){5}[0-9A-Fa-f]{2}$/.test(m); } function TelaListaBranca({ site, sitesSel, openCmd }) { const meta = window.GESTAO_META; const list = sitesSel && sitesSel.length ? sitesSel : [site]; const multi = list.length > 1; const [alvo, setAlvo] = React.useState(list[0]); React.useEffect(() => setAlvo(a => list.includes(a) ? a : list[0]), [list.join(',')]); const s = meta[alvo]; const carga = window.useCarrega(() => Promise.all(list.map(k => window.GestaoAPI.bindings(k) .then(r => (r.bindings || []).map(b => ({ ...b, __k: k, __rot: meta[k].label }))))) .then(a => a.flat()), [list.join(',')]); const vRows = carga.dados || []; const cgCount = vRows.filter(b => b.__k === 'campogas').length; const [mac, setMac] = React.useState(''); const [com, setCom] = React.useState(''); const pronto = macValido(mac) && com.trim().length >= 3; const hoje = new Date().toISOString().slice(0, 10); const incluir = () => openCmd({ titulo: 'Incluir MAC na lista branca', alvo: s.alvo, slug: alvo, acao: 'binding-add', onSucesso: () => carga.recarrega(), mac: mac.toUpperCase(), comment: `CHARLES ${com.trim().toUpperCase()} ${hoje}`, linhas: [mkD(s.alvo, `/ip hotspot ip-binding add mac-address=${mac.toUpperCase()} type=bypassed server=${s.server} comment="CHARLES ${com.trim().toUpperCase()} ${hoje}"`), mkD(s.alvo, `/ip hotspot ip-binding print where mac-address=${mac.toUpperCase()}`)], nota: 'bypassed = nunca passa pelo portal. A segunda linha confere o vínculo criado.' }); const remover = b => { const sb = meta[b.__k]; openCmd({ titulo: 'Remover vínculo da lista branca', alvo: sb.alvo, slug: b.__k, acao: 'binding-del', mac: b.mac, onSucesso: () => carga.recarrega(), linhas: [mkD(sb.alvo, `/ip hotspot ip-binding remove [find where mac-address=${b.mac}]`)], nota: `Vínculo atual: "${b.comment || '(sem comentário)'}". O aparelho volta a cair no portal na próxima conexão.` }); }; return
{list.includes('campogas') ? Rede crítica da empresa — Campo Gás {`${cgCount || 'Os'} vínculos sustentam DVR, relógio de ponto, alarme, impressoras e PCs — 100% do tráfego real. Nunca recriar, renomear ou remover o hotspot server: os vínculos estão amarrados a server=${meta.campogas.server}.`} : null} {multi ? 'Vínculos bypassed — pontos selecionados' : `Vínculos bypassed — ${s.label}`} {multi ? Ponto : null}MACIPComentárioServerAção {vRows.map((b) => {multi ? {b.__rot} : null}{b.mac}{b.ip}{b.comment}{b.server || meta[b.__k].server} remover(b)}>Remover)}
{!vRows.length ?
Nenhum vínculo bypassed neste ponto.
: null}
Incluir MAC {multi ?
Ponto de destino
setAlvo(e.target.value)}>{list.map(k => )}
: null} MAC do aparelho
setMac(e.target.value)} style={{ fontFamily: 'var(--app-font-mono)' }} />
Comentário (obrigatório)
setCom(e.target.value)} />
{`Server fixo do ponto: ${s.server}. O comentário identifica o aparelho no futuro — sem ele, a lista vira adivinhação.`}
Gerar comando
; } Object.assign(window, { TelaInventario, TelaListaBranca });