do tbody representa uma escola
Se não há linhas, encerra sem erros
=================================================== */
var linhas = tabela.querySelectorAll("tbody tr");
if (!linhas.length) return;
/* ===================================================
JS-04 · FUNÇÃO: NORMALIZAR TEXTO
Página Avaliador - Consulta Missões
Remove acentos, converte para minúsculas, elimina espaços duplos
Usada na busca por escola (com ou sem acento)
e no atributo data-escola de cada card
=================================================== */
function normalizar(str) {
return (str || "")
.toLowerCase()
.normalize("NFD")
.replace(/[\u0300-\u036f]/g, "")
.replace(/\s+/g, " ")
.trim();
}
/* ===================================================
JS-05 · FUNÇÃO: COR DA BARRA DE PROGRESSO
Página Avaliador - Consulta Missões
Interpolação RGB contínua:
0% → laranja (#EA7D21)
100% → verde (#2dce89)
Proporcional ao percentual recebido
=================================================== */
function corBarra(pct) {
var r = Math.round(234 + (45 - 234) * (pct / 100));
var g = Math.round(125 + (206 - 125) * (pct / 100));
var b = Math.round( 33 + (137 - 33) * (pct / 100));
return "rgb(" + r + "," + g + "," + b + ")";
}
/* ===================================================
JS-06 · EXTRAIR DADOS DE CADA LINHA DA TABELA
Página Avaliador - Consulta Missões
Estrutura esperada da tabela do plugin:
- 1ª coluna: | ou | com nome da escola
- demais colunas: | com HTML de cada missão
Resultado: array dadosEscolas [{nome, missoes}]
=================================================== */
var dadosEscolas = [];
linhas.forEach(function (tr) {
/* JS-06a · nome da escola — tenta th, cai para td */
var thOuTd = tr.querySelector("th") || tr.querySelector("td");
if (!thOuTd) return;
var nomeEscola = thOuTd.textContent.trim();
/* JS-06b · missões — todos os | , remove o primeiro (nome da escola) */
var tds = Array.from(tr.querySelectorAll("td"));
var tdsMissoes = tds.length > 1 ? tds.slice(1) : tds;
if (!tdsMissoes.length) return;
/* JS-06c · guarda o innerHTML bruto de cada célula de missão */
var missoes = tdsMissoes.map(function (td) {
return { html: td.innerHTML.trim() };
});
dadosEscolas.push({ nome: nomeEscola, missoes: missoes });
});
if (!dadosEscolas.length) return;
var totalEscolas = dadosEscolas.length;
/* ===================================================
JS-07 · CONSTANTE: BASE DE MISSÕES POR ESCOLA
Página Avaliador - Consulta Missões
Valor fixo usado como denominador do percentual
Altere aqui se o programa mudar o número de missões
=================================================== */
var TOTAL_MISSOES_PADRAO = 4;
/* ===================================================
JS-08 · FUNÇÃO: CALCULAR PROGRESSO DA ESCOLA
Página Avaliador - Consulta Missões
Conta missões com texto "avaliado" (excluindo "não avaliado")
Retorna: { concluidas, total, pct }
=================================================== */
function calcularProgresso(missoes) {
var concluidas = missoes.filter(function (m) {
var txt = m.html.toLowerCase();
return txt.includes("avaliado") && !txt.includes("não avaliado");
}).length;
var pct = Math.round((concluidas / TOTAL_MISSOES_PADRAO) * 100);
return { concluidas: concluidas, total: TOTAL_MISSOES_PADRAO, pct: pct };
}
/* ===================================================
JS-09 · FUNÇÃO: STATUS GLOBAL DA ESCOLA
Página Avaliador - Consulta Missões
Determina o data-status do card para os filtros
"concluido" | "andamento" | "avaliar"
=================================================== */
function statusEscola(prog) {
if (prog.pct === 100) return "concluido";
if (prog.concluidas > 0) return "andamento";
return "avaliar";
}
/* ===================================================
JS-10 · FUNÇÃO: CRIAR CARD DE ESCOLA
Página Avaliador - Consulta Missões
Monta o card completo: header + barra + corpo (missões)
Todas as classes correspondem ao CSS separado
=================================================== */
function criarCard(dados) {
var prog = calcularProgresso(dados.missoes);
var status = statusEscola(prog);
/* JS-10a · elemento raiz do card */
var card = document.createElement("div");
card.className = "card-escola-missoes";
card.dataset.status = status;
card.dataset.escola = normalizar(dados.nome);
/* ── JS-10b · HEADER: nome da escola ── */
var header = document.createElement("div");
header.className = "card-escola-header";
var nomeEl = document.createElement("div");
nomeEl.className = "card-escola-nome";
nomeEl.textContent = dados.nome;
/* ── JS-10c · HEADER: barra de progresso ── */
var barraWrap = document.createElement("div");
barraWrap.className = "card-escola-barra-wrap";
var barraFundo = document.createElement("div");
barraFundo.className = "card-escola-barra-fundo";
var barraFill = document.createElement("div");
barraFill.className = "card-escola-barra-fill";
barraFill.style.width = prog.pct + "%";
barraFill.style.background = corBarra(prog.pct);
var barraLabel = document.createElement("span");
barraLabel.className = "card-escola-barra-label";
barraLabel.textContent = prog.pct + "%";
barraFundo.appendChild(barraFill);
barraWrap.appendChild(barraFundo);
barraWrap.appendChild(barraLabel);
header.appendChild(nomeEl);
header.appendChild(barraWrap);
/* ── JS-10d · CORPO: lista de missões ── */
var corpo = document.createElement("div");
corpo.className = "card-escola-body";
dados.missoes.forEach(function (m, idx) {
/* JS-10e · parseia o HTML da célula em elemento temporário
sem renderizar na página — extrai texto e links com segurança */
var tmp = document.createElement("div");
tmp.innerHTML = m.html;
var txtCompleto = tmp.textContent.toLowerCase();
var concluida = txtCompleto.includes("avaliado") &&
!txtCompleto.includes("não avaliado");
/* JS-10f · item da missão com classe de status */
var item = document.createElement("div");
item.className = "escolai-missao-item " +
(concluida ? "status-concluido" : "status-avaliar");
/* JS-10g · título "Missão X"
extrai o número da missão via regex do texto da célula
fallback: usa a posição da coluna (idx + 1) */
var titulo = document.createElement("div");
titulo.className = "missao-titulo";
var matchMissao = txtCompleto.match(/miss[aã]o\s*(\d+)/);
titulo.textContent = matchMissao
? "Missão " + matchMissao[1]
: "Missão " + (idx + 1);
/* JS-10h · subtítulo fixo */
var sub = document.createElement("div");
sub.className = "missao-sub";
sub.textContent = "Registro e Evidências";
/* JS-10i · botão Avaliar
sempre presente — cor e texto mudam conforme status
laranja (.btn-pendente) = não avaliada
verde (.btn-avaliado) = avaliada, pode reavaliar
link original do plugin preservado (answer.php) */
var linkOriginal = tmp.querySelector("a[href*='answer.php']");
var linhaAcao = document.createElement("div");
linhaAcao.className = "missao-linha-acao";
if (linkOriginal) {
var btnAvaliar = document.createElement("a");
btnAvaliar.href = linkOriginal.href;
btnAvaliar.target = linkOriginal.target || "_self";
btnAvaliar.className = "missao-btn-avaliar " +
(concluida ? "btn-avaliado" : "btn-pendente");
btnAvaliar.textContent = concluida ? "✓ Avaliado" : "Avaliar";
linhaAcao.appendChild(btnAvaliar);
}
/* JS-10j · montar item e adicionar ao corpo */
item.appendChild(titulo);
item.appendChild(sub);
item.appendChild(linhaAcao);
corpo.appendChild(item);
});
card.appendChild(header);
card.appendChild(corpo);
return card;
}
/* ===================================================
JS-11 · MONTAR PAINEL DE CONTROLE
Página Avaliador - Consulta Missões
Estrutura: [esq: busca + filtros] | [dir: contadores]
Inserido antes do container de cards no DOM
=================================================== */
var painel = document.createElement("div");
painel.id = "escolai-painel-controle";
/* JS-11a · lado esquerdo */
var esquerda = document.createElement("div");
esquerda.id = "escolai-painel-esquerda";
/* JS-11b · campo de busca por nome da escola */
var campoBusca = document.createElement("input");
campoBusca.id = "escolai-busca-escola";
campoBusca.type = "text";
campoBusca.placeholder = "🔍 Buscar escola...";
/* JS-11c · grupo de botões de filtro por status */
var grupoFiltros = document.createElement("div");
grupoFiltros.id = "escolai-grupo-filtros";
var filtrosConfig = [
{ valor: "all", label: "Todas" },
{ valor: "avaliar", label: "A avaliar" },
{ valor: "andamento", label: "Em andamento" },
{ valor: "concluido", label: "Concluídas" }
];
var botoesStatus = [];
filtrosConfig.forEach(function (cfg) {
var btn = document.createElement("button");
btn.className = "escolai-filtro-btn" + (cfg.valor === "all" ? " ativo" : "");
btn.textContent = cfg.label;
btn.dataset.filtroStatus = cfg.valor;
grupoFiltros.appendChild(btn);
botoesStatus.push(btn);
});
esquerda.appendChild(campoBusca);
esquerda.appendChild(grupoFiltros);
/* JS-11d · lado direito: contador dinâmico (visíveis) */
var direita = document.createElement("div");
direita.id = "escolai-painel-direita";
var contVisiveis = document.createElement("div");
contVisiveis.className = "escolai-contador-bloco";
contVisiveis.innerHTML =
' ' +
totalEscolas +
' Escolas visíveis ';
/* JS-11e · lado direito: contador fixo (total geral) */
var contTotal = document.createElement("div");
contTotal.className = "escolai-contador-bloco";
contTotal.innerHTML =
'' +
totalEscolas +
' Total de escolas ';
direita.appendChild(contVisiveis);
direita.appendChild(contTotal);
painel.appendChild(esquerda);
painel.appendChild(direita);
/* ===================================================
JS-12 · MONTAR CONTAINER DOS CARDS
Página Avaliador - Consulta Missões
Gera um card por escola e registra em cardsGerados[]
para uso posterior pela função de filtros
=================================================== */
var container = document.createElement("div");
container.id = "escolai-cards-container";
var cardsGerados = [];
dadosEscolas.forEach(function (dados) {
var card = criarCard(dados);
container.appendChild(card);
cardsGerados.push(card);
});
/* JS-12a · aviso exibido quando nenhum card passa no filtro */
var avisoVazio = document.createElement("div");
avisoVazio.id = "escolai-aviso-vazio";
avisoVazio.textContent = "🔍 Nenhuma escola encontrada para este filtro.";
container.appendChild(avisoVazio);
/* ===================================================
JS-13 · OCULTAR TABELA E INSERIR NO DOM
Página Avaliador - Consulta Missões
Tabela original do plugin ocultada via classe CSS
Painel e container inseridos antes da tabela no DOM
=================================================== */
tabela.classList.add("escolai-hidden");
var wrapper = document.getElementById("escolai-missoes-wrapper");
wrapper.appendChild(painel);
wrapper.appendChild(container);
/* ===================================================
JS-14 · FUNÇÃO: APLICAR FILTROS
Página Avaliador - Consulta Missões
Busca por nome + filtro por status trabalham em conjunto
Card visível somente se passar nos dois filtros
Atualiza o contador de escolas visíveis em tempo real
=================================================== */
var filtroStatusAtivo = "all";
var textoBusca = "";
function aplicarFiltros() {
var visiveis = 0;
cardsGerados.forEach(function (card) {
/* JS-14a · verifica filtro de busca por nome */
var passaBusca = textoBusca.length === 0 ||
card.dataset.escola.includes(textoBusca);
/* JS-14b · verifica filtro de status */
var passaStatus = filtroStatusAtivo === "all" ||
card.dataset.status === filtroStatusAtivo;
/* JS-14c · mostra ou oculta o card */
var visivel = passaBusca && passaStatus;
card.style.display = visivel ? "" : "none";
if (visivel) visiveis++;
});
/* JS-14d · atualiza contador dinâmico de visíveis */
var el = document.getElementById("escolai-num-visiveis");
if (el) el.textContent = visiveis;
/* JS-14e · exibe aviso se nenhum card visível */
avisoVazio.style.display = visiveis === 0 ? "block" : "none";
}
/* ===================================================
JS-15 · LISTENER: BUSCA EM TEMPO REAL
Página Avaliador - Consulta Missões
Normaliza o texto digitado (remove acentos)
e aplica os filtros a cada tecla pressionada
=================================================== */
campoBusca.addEventListener("input", function () {
textoBusca = normalizar(campoBusca.value);
aplicarFiltros();
});
/* ===================================================
JS-16 · LISTENERS: BOTÕES DE FILTRO POR STATUS
Página Avaliador - Consulta Missões
Remove "ativo" de todos, aplica no clicado
Dispara aplicarFiltros() com o novo status
=================================================== */
botoesStatus.forEach(function (btn) {
btn.addEventListener("click", function () {
botoesStatus.forEach(function (b) { b.classList.remove("ativo"); });
btn.classList.add("ativo");
filtroStatusAtivo = btn.dataset.filtroStatus;
aplicarFiltros();
});
});
/* ===================================================
JS-17 · EXECUÇÃO INICIAL
Página Avaliador - Consulta Missões
Garante estado correto dos filtros ao carregar a página
=================================================== */
aplicarFiltros();
});
|