Ir para o conteúdo principal

Escolaí

Esqueceu o seu usuário ou senha?
Sem HTML e sem CSS inline — apenas lógica JavaScript Depende do CSS: escolai-avaliador-BLOCO-CSS.css Depende do HTML: escolai-avaliador-BLOCO-HTML.html ===================================================== */ /* ===================================================== JS-01 · INICIALIZAÇÃO Página Avaliador - Consulta Missões Aguarda o DOM completo antes de qualquer execução Padrão seguro para o Moodle — evita erros de elementos não encontrados ao rodar o script antes do render ===================================================== */ document.addEventListener("DOMContentLoaded", function () { /* =================================================== JS-02 · LOCALIZAR TABELA DO PLUGIN Página Avaliador - Consulta Missões Seletor: .local_avaliador_table (classe nativa do plugin) Se não existir na página, encerra sem erros =================================================== */ var tabela = document.querySelector(".local_avaliador_table"); if (!tabela) return; /* =================================================== JS-03 · LER LINHAS DO TBODY Página Avaliador - Consulta Missões Cada 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(); });