diff --git a/public/app.js b/public/app.js index d6cb040..84b5d20 100644 --- a/public/app.js +++ b/public/app.js @@ -2680,8 +2680,9 @@ const initApp = () => { const emitirFilterCrianca = document.getElementById('emitirFilterCrianca'); const emitirFilterDataInicio = document.getElementById('emitirFilterDataInicio'); const emitirFilterDataFim = document.getElementById('emitirFilterDataFim'); - const emitirFilterTag = document.getElementById('emitirFilterTag'); + const emitirTagsContainer = document.getElementById('emitirTagsContainer'); const emitirFilterTemplate = document.getElementById('emitirFilterTemplate'); + let emitirSelectedTags = []; const emitirObsCountInfo = document.getElementById('emitirObsCountInfo'); const btnGenerateCompiledReport = document.getElementById('btnGenerateCompiledReport'); @@ -2714,7 +2715,11 @@ const initApp = () => { emitirFilterDataInicio.value = ''; emitirFilterDataFim.value = ''; - emitirFilterTag.value = ''; + + emitirSelectedTags = []; + if (emitirTagsContainer) { + emitirTagsContainer.querySelectorAll('.tag-pill').forEach(p => p.classList.remove('active')); + } await loadFiltersData(); }); @@ -2748,6 +2753,7 @@ const initApp = () => { const res = await fetch(url); const criancas = await res.json(); emitirFilterCrianca.innerHTML = '' + + `` + (Array.isArray(criancas) ? criancas : []).map(c => ``).join(''); } catch (err) { console.error('Erro ao carregar lista de crianças:', err); @@ -2804,25 +2810,42 @@ const initApp = () => { }); } - [emitirFilterCrianca, emitirFilterDataInicio, emitirFilterDataFim, emitirFilterTag, emitirFilterTemplate].forEach(el => { + [emitirFilterCrianca, emitirFilterDataInicio, emitirFilterDataFim, emitirFilterTemplate].forEach(el => { if (el) el.addEventListener('change', updateObservationsCount); }); if (emitirFilterCrianca) { emitirFilterCrianca.addEventListener('input', updateObservationsCount); } + // Adicionar ouvintes para as pills de tags + if (emitirTagsContainer) { + emitirTagsContainer.querySelectorAll('.tag-pill').forEach(pill => { + pill.addEventListener('click', () => { + const tag = pill.getAttribute('data-tag'); + if (pill.classList.contains('active')) { + pill.classList.remove('active'); + emitirSelectedTags = emitirSelectedTags.filter(t => t !== tag); + } else { + pill.classList.add('active'); + emitirSelectedTags.push(tag); + } + updateObservationsCount(); + }); + }); + } + // Recalcular quantidade de observações elegíveis async function updateObservationsCount() { const crianca = emitirFilterCrianca ? emitirFilterCrianca.value : ''; const dataInicio = emitirFilterDataInicio ? emitirFilterDataInicio.value : ''; const dataFim = emitirFilterDataFim ? emitirFilterDataFim.value : ''; - const tag = emitirFilterTag ? emitirFilterTag.value : ''; + const tag = emitirSelectedTags.join(','); const modeloId = emitirFilterTemplate ? emitirFilterTemplate.value : ''; const ano = emitirFilterAno ? emitirFilterAno.value : ''; const turma = emitirFilterTurma ? emitirFilterTurma.value : ''; if (!crianca || !modeloId) { - emitirObsCountInfo.textContent = 'Escolha uma criança e um modelo para prosseguir.'; + emitirObsCountInfo.textContent = 'Escolha uma criança (ou "Todas") e um modelo para prosseguir.'; btnGenerateCompiledReport.disabled = true; btnGenerateCompiledReport.style.opacity = '0.6'; btnGenerateCompiledReport.style.cursor = 'not-allowed'; @@ -2863,7 +2886,7 @@ const initApp = () => { const crianca = emitirFilterCrianca.value; const dataInicio = emitirFilterDataInicio.value; const dataFim = emitirFilterDataFim.value; - const tag = emitirFilterTag.value; + const tags = emitirSelectedTags; const modeloId = emitirFilterTemplate.value; const ano = emitirFilterAno ? emitirFilterAno.value : ''; const turma = emitirFilterTurma ? emitirFilterTurma.value : ''; @@ -2887,7 +2910,7 @@ const initApp = () => { crianca, dataInicio, dataFim, - tags: tag || undefined, + tags: tags.length > 0 ? tags : undefined, modeloId, ano, turma diff --git a/public/index.html b/public/index.html index e1ea5e6..40f56b7 100644 --- a/public/index.html +++ b/public/index.html @@ -574,18 +574,17 @@ -
- - +
+ +
+ + + + + + + +
diff --git a/public/style.css b/public/style.css index 657c01c..9181e0d 100644 --- a/public/style.css +++ b/public/style.css @@ -3012,3 +3012,29 @@ code { background-color: rgba(255, 255, 255, 0.1); transform: scale(1.05); } + +/* Estilo para pills interativas de tags */ +.tag-pill { + background: rgba(255, 255, 255, 0.05); + border: 1px solid var(--border-light); + color: var(--text-secondary, #a3a3a3); + padding: 6px 14px; + border-radius: 20px; + font-size: 0.8rem; + font-weight: 500; + cursor: pointer; + transition: all 0.2s ease; + user-select: none; +} + +.tag-pill:hover { + background: rgba(255, 255, 255, 0.1); + color: var(--text-primary, #ffffff); +} + +.tag-pill.active { + background: rgba(16, 163, 127, 0.2); + border-color: var(--brand-green, #10a37f); + color: var(--brand-green, #10a37f); + font-weight: 600; +} diff --git a/server.js b/server.js index f9064a9..e1b3710 100644 --- a/server.js +++ b/server.js @@ -819,7 +819,7 @@ app.get('/api/observacoes/contar', requireAuth, async (req, res) => { const params = []; let pCount = 1; - if (crianca) { + if (crianca && crianca !== 'todas') { sql += ` AND jsonb_typeof(criancas) = 'array' AND EXISTS ( SELECT 1 FROM jsonb_array_elements_text(criancas) AS c WHERE LOWER(c) LIKE $${pCount} @@ -890,7 +890,7 @@ app.post('/api/modelos/gerar-relatorio', requireAuth, async (req, res) => { const params = []; let pCount = 1; - if (crianca) { + if (crianca && crianca !== 'todas') { sql += ` AND jsonb_typeof(criancas) = 'array' AND EXISTS ( SELECT 1 FROM jsonb_array_elements_text(criancas) AS c WHERE LOWER(c) LIKE $${pCount} @@ -951,8 +951,9 @@ app.post('/api/modelos/gerar-relatorio', requireAuth, async (req, res) => { const agentConfig = readAgentConfig(); const agentName = agentConfig.agentName || "Kemily"; + const isGlobal = crianca === 'todas'; const systemPrompt = `Você é a ${agentName}, uma assistente pedagógica e especialista em educação infantil. -Seu objetivo é analisar o histórico de observações de uma criança e redigir um relatório pedagógico consolidado e profissional. +Seu objetivo é analisar o histórico de observações ${isGlobal ? 'da turma' : 'de uma criança'} e redigir um relatório pedagógico consolidado e profissional ${isGlobal ? 'globalizado da sala de aula / turma' : 'sobre o desenvolvimento individual desta criança'}. Você DEVE estruturar o relatório seguindo exatamente as seções e diretrizes fornecidas no modelo de relatório selecionado. @@ -1058,7 +1059,7 @@ Orientações adicionais: res.json({ report: generatedReport, templateNome: template.nome, - crianca: crianca || 'Todos os alunos', + crianca: crianca === 'todas' ? 'Sala de Aula (Relatório Global)' : (crianca || 'Todos os alunos'), periodo: (dataInicio || '') + (dataInicio && dataFim ? ' a ' : '') + (dataFim || 'Todo o período'), totalObservacoes: observations.length });