diff --git a/public/app.js b/public/app.js index 31a7e8e..8029035 100644 --- a/public/app.js +++ b/public/app.js @@ -6219,7 +6219,7 @@ document.addEventListener('DOMContentLoaded', () => { } if (btnGenerateMindLab) { - btnGenerateMindLab.addEventListener('click', () => { + btnGenerateMindLab.addEventListener('click', async () => { const activeFaixa = document.querySelector('.btn-mindlab-faixa.active'); const activeJogoBtn = document.querySelector('.btn-mindlab-jogo.active'); const activeMetodo = document.querySelector('.btn-mindlab-metodo.active'); @@ -6236,25 +6236,153 @@ document.addEventListener('DOMContentLoaded', () => { const duracaoStr = document.getElementById('mindLabDuracaoSelect').value; const temaLivre = document.getElementById('mindLabTemaLivreInput').value.trim(); - const promptText = `Atue como um especialista na metodologia educacional Mente Inovadora (Mind Lab). Crie um planejamento de aula super detalhado e engajador com os seguintes parâmetros: -- *Faixa Etária:* ${faixaStr} -- *Jogo de Raciocínio:* ${jogoStr} -- *Método Metacognitivo Foco:* ${metodoStr} -- *Foco de Transferência (Socioemocional):* ${focoStr} -- *Duração:* ${duracaoStr}${temaLivre ? '\n- *Tema Contextual:* ' + temaLivre : ''} + const btnG = document.getElementById('btnGenerateMindLab'); + const loader = document.getElementById('mindLabGenerateLoader'); + const resultArea = document.getElementById('mindLabResultArea'); + const output = document.getElementById('mindLabOutput'); + + btnG.style.display = 'none'; + loader.style.display = 'flex'; + resultArea.style.display = 'none'; -Por favor, estruture a aula com: -1. *Abertura (Contextualização):* Como introduzir o método e o tema. -2. *Jogo (Aplicação):* Orientações para a mediação durante a partida. -3. *Reflexão (Mediação Transcendental):* Perguntas provocativas para fazer as crianças refletirem. -4. *Transferência:* Como conectar o que aprenderam no jogo e o método escolhido (ex: Semáforo) com situações reais da vida deles.`; - - mindLabModal.style.display = 'none'; - if (typeof handleSendMessage === 'function') { - handleSendMessage(promptText); - } else { - console.error("handleSendMessage not found"); + try { + const res = await fetch('/api/mindlab/generate', { + method: 'POST', + headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${localStorage.getItem('token')}` }, + body: JSON.stringify({ faixa_etaria: faixaStr, jogo: jogoStr, metodo: metodoStr, foco: focoStr, duracao: duracaoStr, tema: temaLivre }) + }); + const data = await res.json(); + if (!res.ok) throw new Error(data.error || 'Erro na requisição'); + + output.innerHTML = typeof marked !== 'undefined' ? marked.parse(data.planejamento) : data.planejamento; + output.dataset.raw = data.planejamento; + resultArea.style.display = 'block'; + btnG.style.display = 'flex'; + btnG.innerHTML = '🧠 Criar Novo Planejamento'; + } catch (err) { + alert('Erro ao gerar: ' + err.message); + btnG.style.display = 'flex'; + } finally { + loader.style.display = 'none'; } }); } + + const btnCopyMindLab = document.getElementById('btnCopyMindLab'); + if (btnCopyMindLab) { + btnCopyMindLab.addEventListener('click', () => { + const output = document.getElementById('mindLabOutput'); + if (output && output.dataset.raw) { + navigator.clipboard.writeText(output.dataset.raw); + btnCopyMindLab.innerHTML = 'check Copiado!'; + setTimeout(() => { + btnCopyMindLab.innerHTML = 'content_copy Copiar'; + }, 2000); + } + }); + } + + const btnPdfMindLab = document.getElementById('btnPdfMindLab'); + if (btnPdfMindLab) { + btnPdfMindLab.addEventListener('click', () => { + const output = document.getElementById('mindLabOutput'); + if (output && output.dataset.raw && window.jspdf) { + const { jsPDF } = window.jspdf; + const doc = new jsPDF(); + doc.setFontSize(12); + doc.setFont("helvetica", "normal"); + const lines = doc.splitTextToSize(output.dataset.raw.replace(/[#*]/g, ''), 180); + doc.text(lines, 15, 20); + doc.save('Planejamento_Mind_Lab.pdf'); + } else { + alert('Carregando biblioteca PDF ou nenhum conteúdo para salvar.'); + } + }); + } + + const btnOpenMindLabHistory = document.getElementById('btnOpenMindLabHistory'); + const mindLabHistoryModal = document.getElementById('mindLabHistoryModal'); + const btnCloseMindLabHistory = document.getElementById('btnCloseMindLabHistory'); + const mindLabHistoryList = document.getElementById('mindLabHistoryList'); + + if (btnOpenMindLabHistory) { + btnOpenMindLabHistory.addEventListener('click', async () => { + mindLabHistoryModal.style.display = 'flex'; + mindLabHistoryList.innerHTML = '
Carregando histórico...
'; + try { + const res = await fetch('/api/mindlab/list', { + headers: { 'Authorization': `Bearer ${localStorage.getItem('token')}` } + }); + const data = await res.json(); + mindLabHistoryList.innerHTML = ''; + if (data.length === 0) { + mindLabHistoryList.innerHTML = '
Nenhum planejamento salvo ainda.
'; + return; + } + data.forEach(item => { + const div = document.createElement('div'); + div.style.background = 'var(--bg-tertiary)'; + div.style.padding = '12px 15px'; + div.style.borderRadius = '8px'; + div.style.display = 'flex'; + div.style.justifyContent = 'space-between'; + div.style.alignItems = 'center'; + div.innerHTML = ` +
+
🎲 ${item.jogo} - ${item.faixa_etaria}
+
🎯 ${item.foco}
+
${new Date(item.created_at).toLocaleString()}
+
+
+ + +
+ `; + mindLabHistoryList.appendChild(div); + }); + + document.querySelectorAll('.btn-ver-mindlab').forEach(btn => { + btn.addEventListener('click', async (e) => { + const id = e.target.dataset.id; + try { + const res = await fetch(`/api/mindlab/${id}`, { + headers: { 'Authorization': `Bearer ${localStorage.getItem('token')}` } + }); + const itemData = await res.json(); + + document.getElementById('mindLabOutput').innerHTML = typeof marked !== 'undefined' ? marked.parse(itemData.planejamento) : itemData.planejamento; + document.getElementById('mindLabOutput').dataset.raw = itemData.planejamento; + document.getElementById('mindLabResultArea').style.display = 'block'; + + mindLabHistoryModal.style.display = 'none'; + document.getElementById('btnGenerateMindLab').style.display = 'flex'; + } catch (err) { alert('Erro ao carregar planejamento.'); } + }); + }); + + document.querySelectorAll('.btn-del-mindlab').forEach(btn => { + btn.addEventListener('click', async (e) => { + if(!confirm('Tem certeza que deseja apagar este planejamento?')) return; + const id = e.target.dataset.id; + try { + await fetch(`/api/mindlab/${id}`, { + method: 'DELETE', + headers: { 'Authorization': `Bearer ${localStorage.getItem('token')}` } + }); + e.target.closest('div[style*="var(--bg-tertiary)"]').remove(); + } catch (err) { alert('Erro ao deletar.'); } + }); + }); + + } catch (err) { + mindLabHistoryList.innerHTML = '
Erro ao carregar histórico.
'; + } + }); + } + + if (btnCloseMindLabHistory) { + btnCloseMindLabHistory.addEventListener('click', () => { + mindLabHistoryModal.style.display = 'none'; + }); + } }); diff --git a/public/index.html b/public/index.html index f74f025..ad119cd 100644 --- a/public/index.html +++ b/public/index.html @@ -910,7 +910,12 @@

🧠 Planejador de Aulas Mind Lab

- +
+ + +
@@ -982,6 +987,32 @@
Elaborando metodologia e transferência...
+ + +
+ + + + + diff --git a/public/sw.js b/public/sw.js index 734b1dc..deb80e2 100644 --- a/public/sw.js +++ b/public/sw.js @@ -1,4 +1,4 @@ -const CACHE_NAME = 'camila-ai-v4'; +const CACHE_NAME = 'camila-ai-v5'; const urlsToCache = [ '/', '/index.html', diff --git a/server.js b/server.js index c937b2f..41749fe 100644 --- a/server.js +++ b/server.js @@ -1453,6 +1453,86 @@ app.delete('/api/historias/:id', requireAuth, async (req, res) => { } catch (err) { res.status(500).json({ error: 'Erro ao deletar.' }); } }); +// ============================================================ +// ROTAS DO MIND LAB +// ============================================================ +app.post('/api/mindlab/generate', requireAuth, async (req, res) => { + const { faixa_etaria, jogo, metodo, foco, duracao, tema } = req.body; + const usuarioId = req.user?.id || '00000000-0000-0000-0000-000000000000'; + + if (!faixa_etaria || !jogo || !metodo || !foco) { + return res.status(400).json({ error: 'Os campos obrigatórios do planejamento não foram preenchidos.' }); + } + + try { + const systemInstruction = `Você é um especialista na metodologia educacional Mente Inovadora (Mind Lab). +Crie um planejamento de aula detalhado em Markdown com os seguintes parâmetros: +- Faixa Etária: ${faixa_etaria} +- Jogo de Raciocínio: ${jogo} +- Método Metacognitivo Foco: ${metodo} +- Foco de Transferência (Socioemocional): ${foco} +- Duração: ${duracao} +- Tema Contextual: ${tema || 'Nenhum especifico'} + +Por favor, estruture a aula obrigatoriamente com: +1. **Abertura (Contextualização):** Como introduzir o método e o tema. +2. **O Jogo (Aplicação):** Orientações para a mediação durante a partida. +3. **Reflexão (Mediação Transcendental):** Perguntas provocativas para fazer as crianças refletirem. +4. **Transferência:** Como conectar o que aprenderam no jogo e o método escolhido com situações reais da vida deles.`; + + let planejamentoText = ''; + if (process.env.GOOGLE_AI_API_KEY) { + const geminiUrl = `https://generativelanguage.googleapis.com/v1beta/models/${process.env.GOOGLE_AI_MODEL || 'gemini-2.5-flash'}:generateContent?key=${process.env.GOOGLE_AI_API_KEY}`; + const response = await fetch(geminiUrl, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ contents: [{ parts: [{ text: systemInstruction }] }] }) + }); + if (!response.ok) throw new Error('Erro na API Gemini'); + const data = await response.json(); + planejamentoText = data.candidates?.[0]?.content?.parts?.[0]?.text || ''; + } + + planejamentoText = planejamentoText.replace(/[\s\S]*?<\/think>/gi, '').trim(); + + if (!planejamentoText) throw new Error('Falha ao gerar o planejamento.'); + + const dbRes = await dbPool.query( + `INSERT INTO escola.mindlab (usuario_id, faixa_etaria, jogo, metodo, foco, tema, planejamento) VALUES ($1, $2, $3, $4, $5, $6, $7) RETURNING id`, + [usuarioId, faixa_etaria, jogo, metodo, foco, tema, planejamentoText] + ); + + res.json({ id: dbRes.rows[0].id, planejamento: planejamentoText }); + } catch (err) { + console.error('Erro Mind Lab:', err); + res.status(500).json({ error: 'Erro ao gerar planejamento Mind Lab: ' + err.message }); + } +}); + +app.get('/api/mindlab/list', requireAuth, async (req, res) => { + const usuarioId = req.user?.id || '00000000-0000-0000-0000-000000000000'; + try { + const dbRes = await dbPool.query(`SELECT id, faixa_etaria, jogo, metodo, foco, created_at FROM escola.mindlab WHERE usuario_id = $1 ORDER BY created_at DESC`, [usuarioId]); + res.json(dbRes.rows); + } catch (err) { res.status(500).json({ error: 'Erro ao listar histórico do Mind Lab.' }); } +}); + +app.get('/api/mindlab/:id', requireAuth, async (req, res) => { + const usuarioId = req.user?.id || '00000000-0000-0000-0000-000000000000'; + try { + const dbRes = await dbPool.query(`SELECT * FROM escola.mindlab WHERE id = $1 AND usuario_id = $2`, [req.params.id, usuarioId]); + if (dbRes.rows.length === 0) return res.status(404).json({ error: 'Não encontrada.' }); + res.json(dbRes.rows[0]); + } catch (err) { res.status(500).json({ error: 'Erro ao buscar.' }); } +}); + +app.delete('/api/mindlab/:id', requireAuth, async (req, res) => { + const usuarioId = req.user?.id || '00000000-0000-0000-0000-000000000000'; + try { + const dbRes = await dbPool.query(`DELETE FROM escola.mindlab WHERE id = $1 AND usuario_id = $2`, [req.params.id, usuarioId]); + res.json({ success: true }); + } catch (err) { res.status(500).json({ error: 'Erro ao deletar.' }); } +}); // Rota GET para obter configurações do agente app.get('/api/agent-config', requireAuth, (req, res) => { @@ -1677,6 +1757,19 @@ async function initDatabase() { created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW() ); + -- Planejamento Mind Lab + CREATE TABLE IF NOT EXISTS escola.mindlab ( + id UUID PRIMARY KEY DEFAULT gen_random_uuid(), + usuario_id UUID NOT NULL DEFAULT '00000000-0000-0000-0000-000000000000', + faixa_etaria VARCHAR(100) NOT NULL, + jogo VARCHAR(100) NOT NULL, + metodo VARCHAR(100) NOT NULL, + foco VARCHAR(100) NOT NULL, + tema TEXT, + planejamento TEXT NOT NULL, + created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW() + ); + -- Histórias (Inventando Histórias) CREATE TABLE IF NOT EXISTS escola.historias ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(),