fix: refatoracao para visualizacao de notas, provas na lixeira e listagem de funcionarios
This commit is contained in:
parent
bc440d7dbe
commit
e33a5aac3d
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -32,16 +32,16 @@ const Exams: React.FC<ExamsProps> = ({ 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<ExamsProps> = ({ 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<ExamsProps> = ({ 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 });
|
||||
|
|
|
|||
|
|
@ -43,15 +43,18 @@ const ReportCard: React.FC<ReportCardProps> = ({ data, updateData }) => {
|
|||
|
||||
const [dbClasses, setDbClasses] = useState<Class[]>(data.classes || []);
|
||||
const [dbCourses, setDbCourses] = useState<any[]>(data.courses || []);
|
||||
const [dbExams, setDbExams] = useState<any[]>(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<ReportCardProps> = ({ 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<ReportCardProps> = ({ 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<ReportCardProps> = ({ data, updateData }) => {
|
|||
) : (
|
||||
<div className="space-y-6">
|
||||
{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 (
|
||||
<div key={subject.id} className="bg-slate-50 rounded-2xl p-6 border border-slate-100 space-y-4">
|
||||
|
|
@ -665,9 +678,8 @@ const ReportCard: React.FC<ReportCardProps> = ({ data, updateData }) => {
|
|||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
{(() => {
|
||||
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<ReportCardProps> = ({ data, updateData }) => {
|
|||
</div>
|
||||
<div className="flex flex-col gap-6">
|
||||
{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 !== '');
|
||||
|
|
|
|||
|
|
@ -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()}`);
|
||||
}
|
||||
});
|
||||
|
|
@ -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()}`);
|
||||
}
|
||||
});
|
||||
|
|
@ -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()}`);
|
||||
}
|
||||
});
|
||||
|
|
@ -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();
|
||||
|
|
@ -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) {
|
||||
|
|
|
|||
Loading…
Reference in New Issue