edumanagerpro2/manager/scratch/update_exams.js

299 lines
10 KiB
JavaScript

import fs from 'fs';
import path from 'path';
import { fileURLToPath } from 'url';
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
const filePath = path.join(__dirname, '../components/Exams.tsx');
let content = fs.readFileSync(filePath, 'utf8');
// 1. Injetar estado
const stateHook = ` const [dbSubjects, setDbSubjects] = useState<any[]>(data?.subjects || []);`;
const newState = ` const [dbSubjects, setDbSubjects] = useState<any[]>(data?.subjects || []);
const [dbExams, setDbExams] = useState<Exam[]>(data.exams || []);
const loadExams = async () => {
try {
const res = await fetch('/api/provas');
if (res.ok) {
const { provas } = await res.json();
setDbExams(provas.map((p: any) => ({
id: p.id,
classId: p.turma_id,
subjectId: p.disciplina_id,
periodId: p.periodo_id,
title: p.titulo,
durationMinutes: p.duracao_minutos,
status: p.status,
allowRetake: p.permitir_refacao,
isDeleted: p.is_deleted,
evaluationType: p.evaluation_type || 'exam',
questions: [] // questoes carregadas sob demanda
})));
}
} catch(e) {
console.error(e);
}
};
React.useEffect(() => {
loadExams();
}, []);`;
content = content.replace(stateHook, newState);
// 2. Substituir \`const exams = data.exams || [];\`
content = content.replace('const exams = data.exams || [];', 'const exams = dbExams || [];');
// 3. Substituir \`handleEditExam\`
const oldEdit = ` const handleEditExam = (exam: Exam) => {
setEditingExam({ ...exam });
setCurrentView('builder');
};`;
const newEdit = ` const handleEditExam = async (exam: Exam) => {
try {
showAlert('Carregando...', 'Buscando questões da avaliação.', 'info');
const res = await fetch(\`/api/provas/\${exam.id}/questoes\`);
if (res.ok) {
const { questoes } = await res.json();
const mappedQuestions = (questoes || []).map((q: any) => ({
id: q.id,
text: q.texto,
options: typeof q.opcoes === 'string' ? JSON.parse(q.opcoes) : q.opcoes,
correctOptionIndex: q.indice_correto,
imageUrl: q.imagem_url
}));
setEditingExam({ ...exam, questions: mappedQuestions });
setCurrentView('builder');
} else {
showAlert('Erro', 'Não foi possível carregar as questões desta prova.', 'error');
}
} catch(e) {
showAlert('Erro', 'Erro de conexão.', 'error');
}
};`;
content = content.replace(oldEdit, newEdit);
// 4. Substituir funções de save/delete
content = content.replace(
` const handleToggleRetake = (examId: string) => {
const updatedExams = exams.map(e => {
if (e.id === examId) {
return { ...e, allowRetake: !e.allowRetake };
}
return e;
});
updateData({ exams: updatedExams });
dbService.saveData({ ...data, exams: updatedExams });
};`,
` const handleToggleRetake = async (examId: string) => {
const exam = exams.find(e => e.id === examId);
if(!exam) return;
try {
const updated = { ...exam, allowRetake: !exam.allowRetake };
await fetch(\`/api/provas/\${examId}\`, {
method: 'PUT',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(updated)
});
await loadExams();
} catch(e) { console.error(e); }
};`
);
content = content.replace(
` const handleDeleteExam = (examId: string) => {
showConfirm(
'Mover para Lixeira',
'Tem certeza que deseja mover esta avaliação para a lixeira? Ela será ocultada para os alunos, mas as notas no boletim continuarão intactas.',
() => {
const updatedExams = exams.map(e => e.id === examId ? { ...e, isDeleted: true } : e);
updateData({ exams: updatedExams });
dbService.saveData({ ...data, exams: updatedExams });
showAlert('Sucesso', 'Avaliação movida para a lixeira.', 'success');
}
);
};`,
` const handleDeleteExam = (examId: string) => {
showConfirm(
'Mover para Lixeira',
'Tem certeza que deseja mover esta avaliação para a lixeira? Ela será ocultada para os alunos, mas as notas no boletim continuarão intactas.',
async () => {
const exam = exams.find(e => e.id === examId);
if(!exam) return;
try {
await fetch(\`/api/provas/\${examId}\`, {
method: 'PUT',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ ...exam, isDeleted: true })
});
await loadExams();
showAlert('Sucesso', 'Avaliação movida para a lixeira.', 'success');
} catch(e) {}
}
);
};`
);
content = content.replace(
` const handleRestoreExam = (examId: string) => {
const updatedExams = exams.map(e => e.id === examId ? { ...e, isDeleted: false } : e);
updateData({ exams: updatedExams });
dbService.saveData({ ...data, exams: updatedExams });
showAlert('Sucesso', 'Avaliação reativada.', 'success');
};`,
` const handleRestoreExam = async (examId: string) => {
const exam = exams.find(e => e.id === examId);
if(!exam) return;
try {
await fetch(\`/api/provas/\${examId}\`, {
method: 'PUT',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ ...exam, isDeleted: false })
});
await loadExams();
showAlert('Sucesso', 'Avaliação reativada.', 'success');
} catch(e) {}
};`
);
const oldSave = ` const handleSave = (status: 'draft' | 'published') => {
if (!editingExam) return;
if (!editingExam.title || !editingExam.classId) {
showAlert('Atenção', 'Preencha o título e a turma antes de salvar.', 'warning');
return;
}
if (status === 'published' && (!editingExam.subjectId || !editingExam.periodId)) {
showAlert(
'Vínculo Obrigatório',
'Para PUBLICAR a avaliação e permitir que as notas entrem no Boletim Escolar, você precisa vincular uma Disciplina e um Período.',
'warning'
);
return;
}
const finalExam = { ...editingExam, status };
const currentExams = data.exams || [];
const existingIndex = currentExams.findIndex(e => e.id === finalExam.id);
let newExams;
if (existingIndex >= 0) {
newExams = [...currentExams];
newExams[existingIndex] = finalExam;
} else {
newExams = [...currentExams, finalExam];
}
updateData({ exams: newExams });
setCurrentView('list');
setEditingExam(null);
};`;
const newSave = ` const handleSave = async (status: 'draft' | 'published') => {
if (!editingExam) return;
if (!editingExam.title || !editingExam.classId) {
showAlert('Atenção', 'Preencha o título e a turma antes de salvar.', 'warning');
return;
}
if (status === 'published' && (!editingExam.subjectId || !editingExam.periodId)) {
showAlert(
'Vínculo Obrigatório',
'Para PUBLICAR a avaliação e permitir que as notas entrem no Boletim Escolar, você precisa vincular uma Disciplina e um Período.',
'warning'
);
return;
}
const finalExam = { ...editingExam, status };
const isNew = !exams.find(e => e.id === finalExam.id);
try {
showAlert('Aguarde', 'Salvando avaliação...', 'info');
// 1. Salva prova
await fetch(isNew ? '/api/provas' : \`/api/provas/\${finalExam.id}\`, {
method: isNew ? 'POST' : 'PUT',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(finalExam)
});
// 2. Salva questoes
await fetch(\`/api/provas/\${finalExam.id}/questoes\`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ questoes: finalExam.questions })
});
await loadExams();
setCurrentView('list');
setEditingExam(null);
showAlert('Sucesso', 'Avaliação salva com sucesso!', 'success');
} catch(e) {
showAlert('Erro', 'Falha ao salvar avaliação.', 'error');
}
};`;
content = content.replace(oldSave, newSave);
const oldDup = ` const handleDuplicateExam = () => {
if (!duplicatingExam || !targetClassId) return;
const newExam: Exam = {
...duplicatingExam,
id: Date.now().toString() + Math.random().toString(36).substring(7),
classId: targetClassId,
status: 'draft', // Sempre começa como rascunho para segurança
title: \`\${duplicatingExam.title} (Cópia)\`
};
const updatedExams = [...exams, newExam];
updateData({ exams: updatedExams });
dbService.saveData({ ...data, exams: updatedExams });
setDuplicatingExam(null);
setTargetClassId('');
showAlert('Sucesso', 'Avaliação duplicada com sucesso!', 'success');
};`;
const newDup = ` const handleDuplicateExam = async () => {
if (!duplicatingExam || !targetClassId) return;
try {
// 1. Busca questoes originais
const res = await fetch(\`/api/provas/\${duplicatingExam.id}/questoes\`);
const { questoes } = await res.json();
const newExam: Exam = {
...duplicatingExam,
id: crypto.randomUUID(),
classId: targetClassId,
status: 'draft',
title: \`\${duplicatingExam.title} (Cópia)\`
};
await fetch('/api/provas', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(newExam)
});
// 2. Sincroniza as questões no novo id
await fetch(\`/api/provas/\${newExam.id}/questoes\`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ questoes: questoes || [] })
});
await loadExams();
setDuplicatingExam(null);
setTargetClassId('');
showAlert('Sucesso', 'Avaliação duplicada com sucesso!', 'success');
} catch(e) {
showAlert('Erro', 'Falha ao duplicar prova.', 'error');
}
};`;
content = content.replace(oldDup, newDup);
fs.writeFileSync(filePath, content, 'utf8');
console.log('Exams.tsx atualizado com sucesso.');