From e33a5aac3df22f1fe40ca0bd28d33a9b3c2153ee Mon Sep 17 00:00:00 2001 From: Sidney Date: Sun, 24 May 2026 20:09:35 -0300 Subject: [PATCH] fix: refatoracao para visualizacao de notas, provas na lixeira e listagem de funcionarios --- manager/components/Employees.tsx | 10 ++-- manager/components/Exams.tsx | 44 ++++++++++++----- manager/components/ReportCard.tsx | 39 +++++++++------ manager/scratch/find_alunos.js | 8 ++++ manager/scratch/find_exams.js | 8 ++++ manager/scratch/find_getalunos.js | 8 ++++ manager/scratch/migrate_funcionarios.cjs | 61 ++++++++++++++++++++++++ manager/services/database.js | 60 +++++++++++++++++++++-- 8 files changed, 203 insertions(+), 35 deletions(-) create mode 100644 manager/scratch/find_alunos.js create mode 100644 manager/scratch/find_exams.js create mode 100644 manager/scratch/find_getalunos.js create mode 100644 manager/scratch/migrate_funcionarios.cjs diff --git a/manager/components/Employees.tsx b/manager/components/Employees.tsx index b00f728..e592e10 100644 --- a/manager/components/Employees.tsx +++ b/manager/components/Employees.tsx @@ -40,17 +40,17 @@ const Employees: React.FC = () => { // Mapeamento caso a API retorne os nomes das colunas diferentes do TS const mappedEmployees = (empData.funcionarios || []).map((e: any) => ({ id: e.id, - name: e.nome, + name: e.name || e.nome, cpf: e.cpf, email: e.email, - phone: e.telefone, - admissionDate: e.data_admissao ? e.data_admissao.substring(0, 10) : '', - categoryId: e.categoria_id + phone: e.phone || e.telefone, + admissionDate: e.hireDate || (e.data_admissao ? e.data_admissao.substring(0, 10) : ''), + categoryId: e.categoryId || e.categoria_id })); const mappedCategories = (catData.categorias || []).map((c: any) => ({ id: c.id, - name: c.nome + name: c.name || c.nome })); setEmployees(mappedEmployees); diff --git a/manager/components/Exams.tsx b/manager/components/Exams.tsx index 5a4f4d5..1387a73 100644 --- a/manager/components/Exams.tsx +++ b/manager/components/Exams.tsx @@ -32,16 +32,16 @@ const Exams: React.FC = ({ data, updateData }) => { 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, + classId: p.classId || p.turma_id, + subjectId: p.subjectId || p.disciplina_id, + periodId: p.periodId || p.periodo_id, + title: p.title || p.titulo, + durationMinutes: p.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 + allowRetake: p.allowRetake ?? p.permitir_refacao ?? false, + isDeleted: p.isDeleted ?? p.is_deleted ?? false, + evaluationType: p.evaluationType || p.evaluation_type || 'exam', + questions: p.questions || [] }))); } } catch(e) { @@ -137,7 +137,18 @@ const Exams: React.FC = ({ data, updateData }) => { 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 () => { + try { + const targetExam = exams.find(e => e.id === examId); + if (targetExam) { + await fetch(`/api/provas/${examId}`, { + method: 'PUT', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ ...targetExam, isDeleted: true }) + }); + } + } catch (e) { console.error('Erro ao deletar prova:', e); } + const updatedExams = exams.map(e => e.id === examId ? { ...e, isDeleted: true } : e); updateData({ exams: updatedExams }); dbService.saveData({ ...data, exams: updatedExams }); @@ -146,7 +157,18 @@ const Exams: React.FC = ({ data, updateData }) => { ); }; - const handleRestoreExam = (examId: string) => { + const handleRestoreExam = async (examId: string) => { + try { + const targetExam = exams.find(e => e.id === examId); + if (targetExam) { + await fetch(`/api/provas/${examId}`, { + method: 'PUT', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ ...targetExam, isDeleted: false }) + }); + } + } catch (e) { console.error('Erro ao restaurar prova:', e); } + const updatedExams = exams.map(e => e.id === examId ? { ...e, isDeleted: false } : e); updateData({ exams: updatedExams }); dbService.saveData({ ...data, exams: updatedExams }); diff --git a/manager/components/ReportCard.tsx b/manager/components/ReportCard.tsx index 92e3ac5..97dc910 100644 --- a/manager/components/ReportCard.tsx +++ b/manager/components/ReportCard.tsx @@ -43,15 +43,18 @@ const ReportCard: React.FC = ({ data, updateData }) => { const [dbClasses, setDbClasses] = useState(data.classes || []); const [dbCourses, setDbCourses] = useState(data.courses || []); + const [dbExams, setDbExams] = useState(data.exams || []); const loadClassesAndCourses = async () => { try { - const [clsRes, crsRes] = await Promise.all([ + const [clsRes, crsRes, examsRes] = await Promise.all([ fetch('/api/turmas'), - fetch('/api/cursos') + fetch('/api/cursos'), + fetch('/api/provas') ]); const clsData = await clsRes.json(); const crsData = await crsRes.json(); + const examsData = await examsRes.json(); if (clsData.turmas) { setDbClasses(clsData.turmas.map((t: any) => ({ @@ -63,8 +66,19 @@ const ReportCard: React.FC = ({ data, updateData }) => { if (crsData.cursos) { setDbCourses(crsData.cursos); } + if (examsData.provas) { + setDbExams(examsData.provas.map((p: any) => ({ + id: p.id, + classId: p.classId || p.turma_id, + subjectId: p.subjectId || p.disciplina_id, + periodId: p.periodId || p.periodo_id, + title: p.title || p.titulo, + evaluationType: p.evaluationType || p.evaluation_type || 'exam', + isDeleted: p.isDeleted ?? p.is_deleted ?? false + }))); + } } catch(e) { - console.error('Erro ao buscar turmas/cursos:', e); + console.error('Erro ao buscar dados:', e); } }; @@ -255,10 +269,9 @@ const ReportCard: React.FC = ({ data, updateData }) => { initialGrades[subject.id] = {}; periods.forEach(period => { const periodGrades: any = {}; - const linkedExams = (data.exams || []).filter(e => + const linkedExams = (dbExams || []).filter(e => String(e.subjectId).trim() === String(subject.id).trim() && - String(e.periodId).trim() === String(period.id).trim() && - !!subsMap[String(e.id).trim()] + String(e.periodId).trim() === String(period.id).trim() ); if (linkedExams.length > 0) { @@ -653,8 +666,8 @@ const ReportCard: React.FC = ({ data, updateData }) => { ) : (
{subjects.map(subject => { - // Encontrar provas vinculadas a esta disciplina - const linkedExams = (data.exams || []).filter(e => String(e.subjectId).trim() === String(subject.id).trim()); + // Encontrar provas ativas vinculadas a esta disciplina + const linkedExams = (dbExams || []).filter(e => String(e.subjectId).trim() === String(subject.id).trim()); return (
@@ -665,9 +678,8 @@ const ReportCard: React.FC = ({ data, updateData }) => {
{(() => { - const linkedExams = (data.exams || []).filter(e => - String(e.subjectId).trim() === String(subject.id).trim() && - !!studentSubmissions[String(e.id).trim()] + const linkedExams = (dbExams || []).filter(e => + String(e.subjectId).trim() === String(subject.id).trim() ); const provasCount = linkedExams.filter(e => (e as any).evaluationType !== 'activity').length; const atividadesCount = linkedExams.filter(e => (e as any).evaluationType === 'activity').length; @@ -705,10 +717,9 @@ const ReportCard: React.FC = ({ data, updateData }) => {
{periods.map(period => { - const linkedExams = (data.exams || []).filter(e => + const linkedExams = (dbExams || []).filter(e => String(e.subjectId).trim() === String(subject.id).trim() && - String(e.periodId).trim() === String(period.id).trim() && - !!studentSubmissions[String(e.id).trim()] + String(e.periodId).trim() === String(period.id).trim() ); const periodGrades = studentGrades[subject.id]?.[period.id] || {}; const validPeriodValues = Object.values(periodGrades).filter(v => v !== ''); diff --git a/manager/scratch/find_alunos.js b/manager/scratch/find_alunos.js new file mode 100644 index 0000000..5916e34 --- /dev/null +++ b/manager/scratch/find_alunos.js @@ -0,0 +1,8 @@ +import fs from 'fs'; +const content = fs.readFileSync('manager/components/Students.tsx', 'utf8'); +const lines = content.split('\n'); +lines.forEach((l, i) => { + if (l.includes('Alunos')) { + console.log(`[${i+1}] ${l.trim()}`); + } +}); diff --git a/manager/scratch/find_exams.js b/manager/scratch/find_exams.js new file mode 100644 index 0000000..4800650 --- /dev/null +++ b/manager/scratch/find_exams.js @@ -0,0 +1,8 @@ +import fs from 'fs'; +const content = fs.readFileSync('manager/components/ReportCard.tsx', 'utf8'); +const lines = content.split('\n'); +lines.forEach((l, i) => { + if (l.toLowerCase().includes('exam') || l.toLowerCase().includes('prova')) { + console.log(`[${i+1}] ${l.trim()}`); + } +}); diff --git a/manager/scratch/find_getalunos.js b/manager/scratch/find_getalunos.js new file mode 100644 index 0000000..bb82555 --- /dev/null +++ b/manager/scratch/find_getalunos.js @@ -0,0 +1,8 @@ +import fs from 'fs'; +const content = fs.readFileSync('manager/services/database.js', 'utf8'); +const lines = content.split('\n'); +lines.forEach((l, i) => { + if (l.toLowerCase().includes('getalunos')) { + console.log(`[${i+1}] ${l.trim()}`); + } +}); diff --git a/manager/scratch/migrate_funcionarios.cjs b/manager/scratch/migrate_funcionarios.cjs new file mode 100644 index 0000000..595e329 --- /dev/null +++ b/manager/scratch/migrate_funcionarios.cjs @@ -0,0 +1,61 @@ +const { Pool } = require('pg'); + +const pool = new Pool({ + host: '150.230.87.131', + port: 5432, + database: 'edumanager', + user: 'edumanager', + password: 'EduManager2026!Seguro', + ssl: false +}); + +async function migrateFuncionarios() { + const client = await pool.connect(); + try { + const { rows } = await client.query('SELECT data FROM school_data LIMIT 1'); + if (!rows.length) { console.log('school_data vazio!'); return; } + const schoolData = rows[0].data; + + const categories = schoolData.employeeCategories || []; + console.log(`\n📄 Migrando ${categories.length} categorias...`); + let catOk = 0; + for (const c of categories) { + try { + await client.query( + `INSERT INTO categorias_funcionarios (id, nome) VALUES ($1, $2) ON CONFLICT (id) DO NOTHING`, + [c.id, c.name] + ); + catOk++; + } catch (e) { + console.warn(` ⚠️ Categoria ${c.id}: ${e.message}`); + } + } + console.log(` ✅ ${catOk}/${categories.length} categorias migradas!`); + + const employees = schoolData.employees || []; + console.log(`\n👥 Migrando ${employees.length} funcionarios...`); + let empOk = 0; + for (const e of employees) { + try { + await client.query( + `INSERT INTO funcionarios (id, nome, email, telefone, data_admissao, cpf, categoria_id) + VALUES ($1, $2, $3, $4, $5, $6, $7) + ON CONFLICT (id) DO NOTHING`, + [e.id, e.name, e.email || '', e.phone || '', e.hireDate || null, e.cpf || '', e.categoryId || null] + ); + empOk++; + } catch (err) { + console.warn(` ⚠️ Funcionario ${e.id}: ${err.message}`); + } + } + console.log(` ✅ ${empOk}/${employees.length} funcionarios migrados!`); + + } catch (err) { + console.error('❌ ERRO:', err); + } finally { + client.release(); + await pool.end(); + } +} + +migrateFuncionarios(); diff --git a/manager/services/database.js b/manager/services/database.js index c795909..3dece29 100644 --- a/manager/services/database.js +++ b/manager/services/database.js @@ -544,12 +544,25 @@ export async function deleteDisciplina(id) { // ============================================================ export async function getFuncionarios() { const { rows } = await pool.query('SELECT * FROM funcionarios ORDER BY nome ASC'); - return rows; + return rows.map(r => ({ + id: r.id, + name: r.nome, + cpf: r.cpf, + email: r.email, + phone: r.telefone, + categoryId: r.categoria_id, + hireDate: r.data_admissao, + createdAt: r.created_at + })); } export async function getCategoriasFuncionarios() { const { rows } = await pool.query('SELECT * FROM categorias_funcionarios ORDER BY nome ASC'); - return rows; + return rows.map(r => ({ + id: r.id, + name: r.nome, + createdAt: r.created_at + })); } export async function insertFuncionario(f) { @@ -609,7 +622,16 @@ export async function deleteCategoriaFuncionario(id) { // ============================================================ export async function getAlunos() { const result = await pool.query("SELECT * FROM alunos ORDER BY nome ASC"); - return result.rows; + return result.rows.map(r => ({ + ...r, + classId: r.turma_id, + name: r.nome, + status: r.status, + cpf: r.cpf, + phone: r.telefone, + registrationDate: r.data_matricula, + contractTemplateId: r.modelo_contrato_id + })); } export async function insertAluno(a) { @@ -775,8 +797,36 @@ export async function deleteAulas(ids) { // PROVAS & QUESTÕES (FASE 5) // ============================================================ export async function getProvas() { - const result = await pool.query('SELECT * FROM provas ORDER BY created_at DESC'); - return result.rows; + const { rows: provasRows } = await pool.query('SELECT * FROM provas ORDER BY created_at DESC'); + + // Mapear campos para camelCase e buscar questoes para compatibilidade com o frontend antigo + const provasFormatadas = []; + for (const p of provasRows) { + const { rows: questoesRows } = await pool.query('SELECT * FROM questoes_provas WHERE prova_id = $1 ORDER BY ordem ASC', [p.id]); + + provasFormatadas.push({ + 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: questoesRows.map(q => ({ + id: q.id, + examId: q.prova_id, + text: q.texto, + options: q.opcoes || [], + correctAnswer: q.resposta_correta, + order: q.ordem, + imageUrl: q.url_imagem + })) + }); + } + return provasFormatadas; } export async function getQuestoesDaProva(provaId) {