🚀 Auto-deploy: Implementação das ferramentas Garatujas e BrincarAtivo

This commit is contained in:
2026-06-04 23:44:53 +00:00
parent 6866d565ec
commit dcabe19edd
3 changed files with 984 additions and 0 deletions
+557
View File
@@ -4713,9 +4713,566 @@ const initApp = () => {
</body>
</html>
`);
});
}
// ============================================================
// GARATUJAS (ANALISADOR DE TRAÇADO INFANTIL)
// ============================================================
const garatujasModal = document.getElementById('garatujasModal');
const btnCloseGaratujasModal = document.getElementById('btnCloseGaratujasModal');
const barBtnGaratujas = document.getElementById('barBtnGaratujas');
const tabGaratujasNova = document.getElementById('tabGaratujasNova');
const tabGaratujasHistorico = document.getElementById('tabGaratujasHistorico');
const contentGaratujasNova = document.getElementById('contentGaratujasNova');
const contentGaratujasHistorico = document.getElementById('contentGaratujasHistorico');
const garatujasAlunoInput = document.getElementById('garatujasAlunoInput');
const garatujaFileInput = document.getElementById('garatujaFileInput');
const btnUploadGaratuja = document.getElementById('btnUploadGaratuja');
const garatujaFileStatus = document.getElementById('garatujaFileStatus');
const garatujaPreviewContainer = document.getElementById('garatujaPreviewContainer');
const garatujaImgPreview = document.getElementById('garatujaImgPreview');
const btnGenerateGaratuja = document.getElementById('btnGenerateGaratuja');
const garatujaLoader = document.getElementById('garatujaLoader');
const garatujaLoaderText = document.getElementById('garatujaLoaderText');
const garatujaResultArea = document.getElementById('garatujaResultArea');
const garatujaReportArea = document.getElementById('garatujaReportArea');
const btnCopyGaratujaReport = document.getElementById('btnCopyGaratujaReport');
const btnPdfGaratujaReport = document.getElementById('btnPdfGaratujaReport');
const garatujasListContainer = document.getElementById('garatujasListContainer');
const garatujasNoData = document.getElementById('garatujasNoData');
let activeGaratujaReport = null;
// Abas do Garatujas
if (tabGaratujasNova) {
tabGaratujasNova.addEventListener('click', () => {
tabGaratujasNova.classList.add('active');
tabGaratujasHistorico.classList.remove('active');
contentGaratujasNova.style.display = 'block';
contentGaratujasHistorico.style.display = 'none';
});
}
if (tabGaratujasHistorico) {
tabGaratujasHistorico.addEventListener('click', () => {
tabGaratujasHistorico.classList.add('active');
tabGaratujasNova.classList.remove('active');
contentGaratujasNova.style.display = 'none';
contentGaratujasHistorico.style.display = 'block';
loadGaratujasHistory();
});
}
// Abrir Modal
if (barBtnGaratujas) {
barBtnGaratujas.addEventListener('click', () => {
garatujasModal.style.display = 'flex';
garatujasAlunoInput.value = '';
garatujaFileInput.value = '';
garatujaFileStatus.textContent = 'Nenhum arquivo selecionado';
garatujaPreviewContainer.style.display = 'none';
garatujaResultArea.style.display = 'none';
garatujaLoader.style.display = 'none';
btnGenerateGaratuja.disabled = false;
activeGaratujaReport = null;
if (tabGaratujasNova) tabGaratujasNova.click();
});
}
// Fechar Modal
if (btnCloseGaratujasModal) {
btnCloseGaratujasModal.addEventListener('click', () => {
garatujasModal.style.display = 'none';
});
}
window.addEventListener('click', (e) => {
if (e.target === garatujasModal) {
garatujasModal.style.display = 'none';
}
});
// Upload trigger
if (btnUploadGaratuja) {
btnUploadGaratuja.addEventListener('click', () => {
garatujaFileInput.click();
});
}
if (garatujaFileInput) {
garatujaFileInput.addEventListener('change', () => {
const file = garatujaFileInput.files[0];
if (file) {
garatujaFileStatus.textContent = file.name;
const reader = new FileReader();
reader.onload = (e) => {
garatujaImgPreview.src = e.target.result;
garatujaPreviewContainer.style.display = 'block';
};
reader.readAsDataURL(file);
} else {
garatujaFileStatus.textContent = 'Nenhum arquivo selecionado';
garatujaPreviewContainer.style.display = 'none';
}
});
}
// Gerar Análise
if (btnGenerateGaratuja) {
btnGenerateGaratuja.addEventListener('click', async () => {
const alunoName = garatujasAlunoInput.value.trim();
const file = garatujaFileInput.files[0];
if (!alunoName) {
alert('Por favor, informe o nome do aluno.');
return;
}
if (!file) {
alert('Por favor, selecione a foto do desenho.');
return;
}
btnGenerateGaratuja.disabled = true;
garatujaLoader.style.display = 'flex';
garatujaResultArea.style.display = 'none';
garatujaLoaderText.textContent = 'Enviando imagem pedagógica...';
const formData = new FormData();
formData.append('alunoName', alunoName);
formData.append('image', file);
let statusMsgIdx = 0;
const statusMessages = [
'Carregando desenho infantil...',
'IA escaneando os traços e formas...',
'Analisando simetria e coordenação motora...',
'Definindo estágio evolutivo do desenho...',
'Criando sugestões de atividades formativas...',
'Finalizando laudo psicopedagógico...'
];
const statusInterval = setInterval(() => {
if (statusMsgIdx < statusMessages.length) {
garatujaLoaderText.textContent = statusMessages[statusMsgIdx];
statusMsgIdx++;
} else {
garatujaLoaderText.textContent = 'Formatando parecer...';
}
}, 4500);
try {
const response = await fetch('/api/garatujas/analyze', {
method: 'POST',
body: formData
});
clearInterval(statusInterval);
if (!response.ok) {
const errData = await response.json();
throw new Error(errData.error || 'Erro desconhecido');
}
const data = await response.json();
activeGaratujaReport = data;
if (window.marked) {
garatujaReportArea.innerHTML = marked.parse(data.analise);
} else {
garatujaReportArea.textContent = data.analise;
}
garatujaLoader.style.display = 'none';
garatujaResultArea.style.display = 'flex';
btnGenerateGaratuja.disabled = false;
} catch (err) {
clearInterval(statusInterval);
console.error(err);
alert('Erro na análise: ' + err.message);
garatujaLoader.style.display = 'none';
btnGenerateGaratuja.disabled = false;
}
});
}
// Copiar Parecer Garatujas
if (btnCopyGaratujaReport) {
btnCopyGaratujaReport.addEventListener('click', () => {
if (activeGaratujaReport && activeGaratujaReport.analise) {
navigator.clipboard.writeText(activeGaratujaReport.analise)
.then(() => {
btnCopyGaratujaReport.textContent = '✅ Copiado!';
setTimeout(() => { btnCopyGaratujaReport.textContent = '📋 Copiar'; }, 2000);
});
}
});
}
// Baixar PDF Garatujas
if (btnPdfGaratujaReport) {
btnPdfGaratujaReport.addEventListener('click', () => {
if (!activeGaratujaReport) return;
const printWindow = window.open('', '_blank');
printWindow.document.write(`
<html>
<head>
<title>Garatujas - Laudo do Desenho</title>
<style>
body { font-family: 'Outfit', 'Inter', sans-serif; padding: 40px; color: #333; line-height: 1.6; }
h1, h2, h3 { color: #ea580c; margin-top: 24px; margin-bottom: 12px; }
h1 { border-bottom: 2px solid #e2e8f0; padding-bottom: 10px; font-size: 22px; }
.meta-box { background: #f8fafc; padding: 16px; border-radius: 8px; margin-bottom: 24px; border: 1px solid #e2e8f0; display: flex; gap: 16px; align-items: center; }
.meta-label { font-weight: bold; color: #475569; }
img { border-radius: 6px; border: 1px solid #ddd; max-height: 120px; }
@media print {
body { padding: 0; }
button { display: none; }
}
</style>
</head>
<body>
<h1>🎨 Garatujas - Parecer de Traçado Infantil</h1>
<div class="meta-box">
<img src="${activeGaratujaReport.imageUrl}">
<div>
<div><span class="meta-label">Aluno(a):</span> <span>${activeGaratujaReport.alunoName}</span></div>
<div><span class="meta-label">Data de Análise:</span> <span>${new Date(activeGaratujaReport.createdAt).toLocaleDateString('pt-BR')}</span></div>
</div>
</div>
<div>${window.marked ? marked.parse(activeGaratujaReport.analise) : activeGaratujaReport.analise}</div>
<script>
window.onload = function() {
window.print();
setTimeout(() => { window.close(); }, 500);
};
<\/script>
</body>
</html>
`);
printWindow.document.close();
});
}
// Carregar Histórico Garatujas
async function loadGaratujasHistory() {
try {
const response = await fetch('/api/garatujas/list');
if (!response.ok) throw new Error('Erro ao carregar histórico');
const list = await response.json();
garatujasListContainer.innerHTML = '';
if (list.length === 0) {
garatujasNoData.style.display = 'block';
return;
}
garatujasNoData.style.display = 'none';
list.forEach(item => {
const div = document.createElement('div');
div.className = 'quick-note-card';
div.style.cursor = 'pointer';
div.style.display = 'flex';
div.style.flexDirection = 'column';
div.style.gap = '8px';
div.style.padding = '12px';
div.innerHTML = `
<div style="width: 100%; aspect-ratio: 4/3; overflow: hidden; border-radius: 6px; background: rgba(0,0,0,0.2);">
<img src="${item.imageUrl}" style="width: 100%; height: 100%; object-fit: cover;">
</div>
<div style="font-weight: 600; color: var(--text-primary); font-size: 0.9rem;">${escapeHtml(item.alunoName)}</div>
<div style="font-size: 0.75rem; color: var(--text-muted);">${new Date(item.createdAt).toLocaleDateString('pt-BR')}</div>
`;
div.addEventListener('click', async () => {
try {
const detailResp = await fetch(`/api/garatujas/${item.id}`);
if (!detailResp.ok) throw new Error('Erro ao obter detalhes');
const detail = await detailResp.json();
activeGaratujaReport = detail;
garatujasAlunoInput.value = detail.alunoName;
garatujaImgPreview.src = detail.imageUrl;
garatujaPreviewContainer.style.display = 'block';
garatujaReportArea.innerHTML = window.marked ? marked.parse(detail.analise) : detail.analise;
garatujaResultArea.style.display = 'flex';
if (tabGaratujasNova) tabGaratujasNova.click();
} catch (e) {
alert('Falha ao abrir desenho.');
}
});
garatujasListContainer.appendChild(div);
});
} catch (err) {
console.error(err);
}
}
// ============================================================
// BRINCARATIVO (CIRCUITOS PSICOMOTORES E BRINCADEIRAS)
// ============================================================
const brincarAtivoModal = document.getElementById('brincarAtivoModal');
const btnCloseBrincarAtivoModal = document.getElementById('btnCloseBrincarAtivoModal');
const barBtnBrincarAtivo = document.getElementById('barBtnBrincarAtivo');
const tabBrincarAtivoNova = document.getElementById('tabBrincarAtivoNova');
const tabBrincarAtivoHistorico = document.getElementById('tabBrincarAtivoHistorico');
const contentBrincarAtivoNova = document.getElementById('contentBrincarAtivoNova');
const contentBrincarAtivoHistorico = document.getElementById('contentBrincarAtivoHistorico');
const brincarAtivoFaixaSelect = document.getElementById('brincarAtivoFaixaSelect');
const brincarAtivoRecursosInput = document.getElementById('brincarAtivoRecursosInput');
const btnGenerateBrincarAtivo = document.getElementById('btnGenerateBrincarAtivo');
const brincarAtivoLoader = document.getElementById('brincarAtivoLoader');
const brincarAtivoLoaderText = document.getElementById('brincarAtivoLoaderText');
const brincarAtivoResultArea = document.getElementById('brincarAtivoResultArea');
const brincarAtivoReportArea = document.getElementById('brincarAtivoReportArea');
const btnCopyBrincarAtivoReport = document.getElementById('btnCopyBrincarAtivoReport');
const btnPdfBrincarAtivoReport = document.getElementById('btnPdfBrincarAtivoReport');
const brincarAtivoListContainer = document.getElementById('brincarAtivoListContainer');
const brincarAtivoNoData = document.getElementById('brincarAtivoNoData');
let activeBrincarAtivoReport = null;
// Abas do BrincarAtivo
if (tabBrincarAtivoNova) {
tabBrincarAtivoNova.addEventListener('click', () => {
tabBrincarAtivoNova.classList.add('active');
tabBrincarAtivoHistorico.classList.remove('active');
contentBrincarAtivoNova.style.display = 'block';
contentBrincarAtivoHistorico.style.display = 'none';
});
}
if (tabBrincarAtivoHistorico) {
tabBrincarAtivoHistorico.addEventListener('click', () => {
tabBrincarAtivoHistorico.classList.add('active');
tabBrincarAtivoNova.classList.remove('active');
contentBrincarAtivoNova.style.display = 'none';
contentBrincarAtivoHistorico.style.display = 'block';
loadBrincarAtivoHistory();
});
}
// Abrir Modal
if (barBtnBrincarAtivo) {
barBtnBrincarAtivo.addEventListener('click', () => {
brincarAtivoModal.style.display = 'flex';
brincarAtivoRecursosInput.value = '';
brincarAtivoResultArea.style.display = 'none';
brincarAtivoLoader.style.display = 'none';
btnGenerateBrincarAtivo.disabled = false;
activeBrincarAtivoReport = null;
if (tabBrincarAtivoNova) tabBrincarAtivoNova.click();
});
}
// Fechar Modal
if (btnCloseBrincarAtivoModal) {
btnCloseBrincarAtivoModal.addEventListener('click', () => {
brincarAtivoModal.style.display = 'none';
});
}
window.addEventListener('click', (e) => {
if (e.target === brincarAtivoModal) {
brincarAtivoModal.style.display = 'none';
}
});
// Gerar Atividade
if (btnGenerateBrincarAtivo) {
btnGenerateBrincarAtivo.addEventListener('click', async () => {
const faixaEtaria = brincarAtivoFaixaSelect.value;
const recursos = brincarAtivoRecursosInput.value.trim();
if (!recursos) {
alert('Por favor, informe os materiais ou recursos disponíveis.');
return;
}
btnGenerateBrincarAtivo.disabled = true;
brincarAtivoLoader.style.display = 'flex';
brincarAtivoResultArea.style.display = 'none';
brincarAtivoLoaderText.textContent = 'Acessando referências BNCC...';
let statusMsgIdx = 0;
const statusMessages = [
'Analisando faixa etária psicomotora...',
'Estruturando proposta lúdica de circuito...',
'Conectando objetivos específicos da BNCC...',
'Gerando instruções passo a passo para o professor...',
'Finalizando plano de circuito ativo...'
];
const statusInterval = setInterval(() => {
if (statusMsgIdx < statusMessages.length) {
brincarAtivoLoaderText.textContent = statusMessages[statusMsgIdx];
statusMsgIdx++;
} else {
brincarAtivoLoaderText.textContent = 'Formatando atividade...';
}
}, 4500);
try {
const response = await fetch('/api/brincar-ativo/generate', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ faixaEtaria, recursos })
});
clearInterval(statusInterval);
if (!response.ok) {
const errData = await response.json();
throw new Error(errData.error || 'Erro desconhecido');
}
const data = await response.json();
activeBrincarAtivoReport = data;
if (window.marked) {
brincarAtivoReportArea.innerHTML = marked.parse(data.atividade);
} else {
brincarAtivoReportArea.textContent = data.atividade;
}
brincarAtivoLoader.style.display = 'none';
brincarAtivoResultArea.style.display = 'flex';
btnGenerateBrincarAtivo.disabled = false;
} catch (err) {
clearInterval(statusInterval);
console.error(err);
alert('Erro ao gerar atividade: ' + err.message);
brincarAtivoLoader.style.display = 'none';
btnGenerateBrincarAtivo.disabled = false;
}
});
}
// Copiar Parecer BrincarAtivo
if (btnCopyBrincarAtivoReport) {
btnCopyBrincarAtivoReport.addEventListener('click', () => {
if (activeBrincarAtivoReport && activeBrincarAtivoReport.atividade) {
navigator.clipboard.writeText(activeBrincarAtivoReport.atividade)
.then(() => {
btnCopyBrincarAtivoReport.textContent = '✅ Copiado!';
setTimeout(() => { btnCopyBrincarAtivoReport.textContent = '📋 Copiar'; }, 2000);
});
}
});
}
// Baixar PDF BrincarAtivo
if (btnPdfBrincarAtivoReport) {
btnPdfBrincarAtivoReport.addEventListener('click', () => {
if (!activeBrincarAtivoReport) return;
const printWindow = window.open('', '_blank');
printWindow.document.write(`
<html>
<head>
<title>BrincarAtivo - Roteiro de Circuito</title>
<style>
body { font-family: 'Outfit', 'Inter', sans-serif; padding: 40px; color: #333; line-height: 1.6; }
h1, h2, h3 { color: #10a37f; margin-top: 24px; margin-bottom: 12px; }
h1 { border-bottom: 2px solid #e2e8f0; padding-bottom: 10px; font-size: 22px; }
.meta-box { background: #f8fafc; padding: 16px; border-radius: 8px; margin-bottom: 24px; border: 1px solid #e2e8f0; }
.meta-label { font-weight: bold; color: #475569; }
@media print {
body { padding: 0; }
button { display: none; }
}
</style>
</head>
<body>
<h1>🏃‍♂️ BrincarAtivo - Planejamento Psicomotor</h1>
<div class="meta-box">
<div><span class="meta-label">Faixa Etária:</span> <span>${activeBrincarAtivoReport.faixaEtaria}</span></div>
<div><span class="meta-label">Recursos:</span> <span>${activeBrincarAtivoReport.recursos}</span></div>
<div><span class="meta-label">Data de Criação:</span> <span>${new Date(activeBrincarAtivoReport.createdAt).toLocaleDateString('pt-BR')}</span></div>
</div>
<div>${window.marked ? marked.parse(activeBrincarAtivoReport.atividade) : activeBrincarAtivoReport.atividade}</div>
<script>
window.onload = function() {
window.print();
setTimeout(() => { window.close(); }, 500);
};
<\/script>
</body>
</html>
`);
printWindow.document.close();
});
}
// Carregar Histórico BrincarAtivo
async function loadBrincarAtivoHistory() {
try {
const response = await fetch('/api/brincar-ativo/list');
if (!response.ok) throw new Error('Erro ao carregar histórico');
const list = await response.json();
brincarAtivoListContainer.innerHTML = '';
if (list.length === 0) {
brincarAtivoNoData.style.display = 'block';
return;
}
brincarAtivoNoData.style.display = 'none';
list.forEach(item => {
const div = document.createElement('div');
div.className = 'quick-note-card';
div.style.cursor = 'pointer';
div.style.display = 'flex';
div.style.flexDirection = 'column';
div.style.gap = '6px';
div.style.padding = '12px';
const lines = item.recursos || '';
const previewText = lines.length > 60 ? lines.substring(0, 60) + '...' : lines;
div.innerHTML = `
<div style="font-weight: 600; color: var(--text-primary); font-size: 0.92rem; display: flex; align-items: center; gap: 6px;">
<span>🏃‍♂️</span> ${escapeHtml(item.faixaEtaria.split(' ')[0])} (${item.faixaEtaria.includes('bem pequenas') ? 'Bem Pequenas' : (item.faixaEtaria.includes('pequenas') ? 'Pequenas' : 'Bebês')})
</div>
<div style="font-size: 0.8rem; color: var(--text-secondary);">Recursos: ${escapeHtml(previewText)}</div>
<div style="font-size: 0.75rem; color: var(--text-muted);">${new Date(item.createdAt).toLocaleDateString('pt-BR')}</div>
`;
div.addEventListener('click', async () => {
try {
const detailResp = await fetch(`/api/brincar-ativo/${item.id}`);
if (!detailResp.ok) throw new Error('Erro ao obter detalhes');
const detail = await detailResp.json();
activeBrincarAtivoReport = detail;
brincarAtivoFaixaSelect.value = detail.faixaEtaria;
brincarAtivoRecursosInput.value = detail.recursos;
brincarAtivoReportArea.innerHTML = window.marked ? marked.parse(detail.atividade) : detail.atividade;
brincarAtivoResultArea.style.display = 'flex';
if (tabBrincarAtivoNova) tabBrincarAtivoNova.click();
} catch (e) {
alert('Falha ao abrir atividade.');
}
});
brincarAtivoListContainer.appendChild(div);
});
} catch (err) {
console.error(err);
}
}
};
// Player de áudio personalizado global para as mídias geradas