Adicionado fluxo de revisão de transcrição de voz antes da análise de IA
This commit is contained in:
+115
-28
@@ -1993,6 +1993,14 @@ const initApp = () => {
|
||||
const btnDiscardReport = document.getElementById('btnDiscardReport');
|
||||
const btnSaveReport = document.getElementById('btnSaveReport');
|
||||
|
||||
// Novos elementos de transcrição
|
||||
const recordStateTranscribed = document.getElementById('recordStateTranscribed');
|
||||
const transcriptionTextarea = document.getElementById('transcriptionTextarea');
|
||||
const btnDiscardTranscription = document.getElementById('btnDiscardTranscription');
|
||||
const btnAnalyzeTranscription = document.getElementById('btnAnalyzeTranscription');
|
||||
const recordProcessingText = document.getElementById('recordProcessingText');
|
||||
const recordProcessingSubtext = document.getElementById('recordProcessingSubtext');
|
||||
|
||||
let mediaRecorder = null;
|
||||
let audioChunks = [];
|
||||
let recordingStartTime = null;
|
||||
@@ -2002,6 +2010,40 @@ const initApp = () => {
|
||||
let generatedTurma = 'não informada';
|
||||
let generatedTags = [];
|
||||
|
||||
// Fechar modal - colocada no topo para evitar problemas de inicialização
|
||||
function closeRecordModal() {
|
||||
if (mediaRecorder && mediaRecorder.state !== 'inactive') {
|
||||
try {
|
||||
mediaRecorder.stop();
|
||||
} catch (e) {}
|
||||
if (mediaRecorder.stream) {
|
||||
mediaRecorder.stream.getTracks().forEach(t => t.stop());
|
||||
}
|
||||
}
|
||||
clearInterval(timerInterval);
|
||||
audioChunks = [];
|
||||
generatedReport = null;
|
||||
recordModal.style.display = 'none';
|
||||
}
|
||||
|
||||
// Função para controlar a exibição dos estados
|
||||
function showRecordState(state) {
|
||||
recordStateRecording.style.display = 'none';
|
||||
recordStateProcessing.style.display = 'none';
|
||||
if (recordStateTranscribed) recordStateTranscribed.style.display = 'none';
|
||||
recordStatePreview.style.display = 'none';
|
||||
|
||||
if (state === 'recording') {
|
||||
recordStateRecording.style.display = 'flex';
|
||||
} else if (state === 'processing') {
|
||||
recordStateProcessing.style.display = 'flex';
|
||||
} else if (state === 'transcribed') {
|
||||
if (recordStateTranscribed) recordStateTranscribed.style.display = 'flex';
|
||||
} else if (state === 'preview') {
|
||||
recordStatePreview.style.display = 'flex';
|
||||
}
|
||||
}
|
||||
|
||||
// Abrir modal e iniciar gravação
|
||||
if (fabRecord) {
|
||||
fabRecord.addEventListener('click', async () => {
|
||||
@@ -2009,9 +2051,7 @@ const initApp = () => {
|
||||
generatedReport = null;
|
||||
|
||||
// Mostra estado inicial (gravando)
|
||||
recordStateRecording.style.display = 'flex';
|
||||
recordStateProcessing.style.display = 'none';
|
||||
recordStatePreview.style.display = 'none';
|
||||
showRecordState('recording');
|
||||
recordModal.style.display = 'flex';
|
||||
|
||||
// Inicia timer
|
||||
@@ -2056,20 +2096,25 @@ const initApp = () => {
|
||||
const mins = Math.floor(elapsed / 60).toString().padStart(2, '0');
|
||||
const secs = (elapsed % 60).toString().padStart(2, '0');
|
||||
recordTimer.textContent = `${mins}:${secs}`;
|
||||
};
|
||||
}
|
||||
|
||||
// Parar gravação e processar
|
||||
// Parar gravação e transcrever
|
||||
if (btnStopRecord) {
|
||||
btnStopRecord.addEventListener('click', async () => {
|
||||
if (mediaRecorder && mediaRecorder.state !== 'inactive') {
|
||||
mediaRecorder.stop();
|
||||
mediaRecorder.stream.getTracks().forEach(t => t.stop());
|
||||
try {
|
||||
mediaRecorder.stop();
|
||||
} catch (e) {}
|
||||
if (mediaRecorder.stream) {
|
||||
mediaRecorder.stream.getTracks().forEach(t => t.stop());
|
||||
}
|
||||
}
|
||||
clearInterval(timerInterval);
|
||||
|
||||
// Mostra processando
|
||||
recordStateRecording.style.display = 'none';
|
||||
recordStateProcessing.style.display = 'flex';
|
||||
// Mostra processando com texto de transcrição
|
||||
if (recordProcessingText) recordProcessingText.textContent = 'Transcrevendo áudio...';
|
||||
if (recordProcessingSubtext) recordProcessingSubtext.textContent = 'Aguarde a transcrição por Whisper';
|
||||
showRecordState('processing');
|
||||
|
||||
// Envia áudio para servidor
|
||||
const blob = new Blob(audioChunks, { type: 'audio/webm' });
|
||||
@@ -2081,6 +2126,54 @@ const initApp = () => {
|
||||
method: 'POST',
|
||||
body: formData
|
||||
});
|
||||
|
||||
if (!res.ok) {
|
||||
const errData = await res.json();
|
||||
throw new Error(errData.error || 'Erro na transcrição');
|
||||
}
|
||||
|
||||
const data = await res.json();
|
||||
|
||||
// Exibe o texto transcrito na área de texto para edição
|
||||
if (transcriptionTextarea) {
|
||||
transcriptionTextarea.value = data.transcribedText || '';
|
||||
}
|
||||
|
||||
showRecordState('transcribed');
|
||||
} catch (err) {
|
||||
console.error('Erro ao processar observação:', err);
|
||||
alert('Erro ao transcrever áudio: ' + err.message);
|
||||
closeRecordModal();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Ação de analisar transcrição
|
||||
if (btnAnalyzeTranscription) {
|
||||
btnAnalyzeTranscription.addEventListener('click', async () => {
|
||||
const text = transcriptionTextarea ? transcriptionTextarea.value.trim() : '';
|
||||
if (!text || text.length < 5) {
|
||||
alert('Por favor, digite ou revise a transcrição (mínimo de 5 caracteres).');
|
||||
return;
|
||||
}
|
||||
|
||||
// Mostra processando com texto de análise
|
||||
if (recordProcessingText) recordProcessingText.textContent = 'Analisando observação...';
|
||||
if (recordProcessingSubtext) recordProcessingSubtext.textContent = 'Gerando relatório pedagógico estruturado';
|
||||
showRecordState('processing');
|
||||
|
||||
try {
|
||||
const res = await fetch('/api/observacao/analisar', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ transcribedText: text })
|
||||
});
|
||||
|
||||
if (!res.ok) {
|
||||
const errData = await res.json();
|
||||
throw new Error(errData.error || 'Erro na análise');
|
||||
}
|
||||
|
||||
const data = await res.json();
|
||||
generatedReport = data.relatorio || data.report;
|
||||
generatedCriancas = data.criancas;
|
||||
@@ -2088,17 +2181,23 @@ const initApp = () => {
|
||||
generatedTags = data.tags || [];
|
||||
showReportPreview(data);
|
||||
} catch (err) {
|
||||
console.error('Erro ao processar observação:', err);
|
||||
alert('Erro ao processar sua observação. Tente novamente.');
|
||||
closeRecordModal();
|
||||
console.error('Erro ao analisar observação:', err);
|
||||
alert('Erro ao analisar observação: ' + err.message);
|
||||
showRecordState('transcribed');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Ação de descartar transcrição
|
||||
if (btnDiscardTranscription) {
|
||||
btnDiscardTranscription.addEventListener('click', () => {
|
||||
closeRecordModal();
|
||||
});
|
||||
}
|
||||
|
||||
// Mostrar prévia do relatório
|
||||
function showReportPreview(data) {
|
||||
recordStateProcessing.style.display = 'none';
|
||||
recordStatePreview.style.display = 'flex';
|
||||
showRecordState('preview');
|
||||
|
||||
const now = new Date();
|
||||
previewDateTime.textContent = now.toLocaleString('pt-BR', {
|
||||
@@ -2123,7 +2222,7 @@ const initApp = () => {
|
||||
${marked.parse(generatedReport || 'Relatório não disponível.')}
|
||||
`;
|
||||
reportContent.querySelectorAll('pre code').forEach(b => hljs.highlightElement(b));
|
||||
};
|
||||
}
|
||||
|
||||
// Descartar relatório
|
||||
if (btnDiscardReport) {
|
||||
@@ -2173,18 +2272,6 @@ const initApp = () => {
|
||||
btnExitRecord.addEventListener('click', closeRecordModal);
|
||||
}
|
||||
|
||||
// Fechar modal
|
||||
function closeRecordModal() {
|
||||
if (mediaRecorder && mediaRecorder.state !== 'inactive') {
|
||||
mediaRecorder.stop();
|
||||
mediaRecorder.stream.getTracks().forEach(t => t.stop());
|
||||
}
|
||||
clearInterval(timerInterval);
|
||||
audioChunks = [];
|
||||
generatedReport = null;
|
||||
recordModal.style.display = 'none';
|
||||
};
|
||||
|
||||
// Fechar clicando fora
|
||||
if (recordModal) {
|
||||
recordModal.addEventListener('click', (e) => {
|
||||
|
||||
Reference in New Issue
Block a user