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
|
// Mapeamento caso a API retorne os nomes das colunas diferentes do TS
|
||||||
const mappedEmployees = (empData.funcionarios || []).map((e: any) => ({
|
const mappedEmployees = (empData.funcionarios || []).map((e: any) => ({
|
||||||
id: e.id,
|
id: e.id,
|
||||||
name: e.nome,
|
name: e.name || e.nome,
|
||||||
cpf: e.cpf,
|
cpf: e.cpf,
|
||||||
email: e.email,
|
email: e.email,
|
||||||
phone: e.telefone,
|
phone: e.phone || e.telefone,
|
||||||
admissionDate: e.data_admissao ? e.data_admissao.substring(0, 10) : '',
|
admissionDate: e.hireDate || (e.data_admissao ? e.data_admissao.substring(0, 10) : ''),
|
||||||
categoryId: e.categoria_id
|
categoryId: e.categoryId || e.categoria_id
|
||||||
}));
|
}));
|
||||||
|
|
||||||
const mappedCategories = (catData.categorias || []).map((c: any) => ({
|
const mappedCategories = (catData.categorias || []).map((c: any) => ({
|
||||||
id: c.id,
|
id: c.id,
|
||||||
name: c.nome
|
name: c.name || c.nome
|
||||||
}));
|
}));
|
||||||
|
|
||||||
setEmployees(mappedEmployees);
|
setEmployees(mappedEmployees);
|
||||||
|
|
|
||||||
|
|
@ -32,16 +32,16 @@ const Exams: React.FC<ExamsProps> = ({ data, updateData }) => {
|
||||||
const { provas } = await res.json();
|
const { provas } = await res.json();
|
||||||
setDbExams(provas.map((p: any) => ({
|
setDbExams(provas.map((p: any) => ({
|
||||||
id: p.id,
|
id: p.id,
|
||||||
classId: p.turma_id,
|
classId: p.classId || p.turma_id,
|
||||||
subjectId: p.disciplina_id,
|
subjectId: p.subjectId || p.disciplina_id,
|
||||||
periodId: p.periodo_id,
|
periodId: p.periodId || p.periodo_id,
|
||||||
title: p.titulo,
|
title: p.title || p.titulo,
|
||||||
durationMinutes: p.duracao_minutos,
|
durationMinutes: p.durationMinutes || p.duracao_minutos,
|
||||||
status: p.status,
|
status: p.status,
|
||||||
allowRetake: p.permitir_refacao,
|
allowRetake: p.allowRetake ?? p.permitir_refacao ?? false,
|
||||||
isDeleted: p.is_deleted,
|
isDeleted: p.isDeleted ?? p.is_deleted ?? false,
|
||||||
evaluationType: p.evaluation_type || 'exam',
|
evaluationType: p.evaluationType || p.evaluation_type || 'exam',
|
||||||
questions: [] // questoes carregadas sob demanda
|
questions: p.questions || []
|
||||||
})));
|
})));
|
||||||
}
|
}
|
||||||
} catch(e) {
|
} catch(e) {
|
||||||
|
|
@ -137,7 +137,18 @@ const Exams: React.FC<ExamsProps> = ({ data, updateData }) => {
|
||||||
showConfirm(
|
showConfirm(
|
||||||
'Mover para Lixeira',
|
'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.',
|
'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);
|
const updatedExams = exams.map(e => e.id === examId ? { ...e, isDeleted: true } : e);
|
||||||
updateData({ exams: updatedExams });
|
updateData({ exams: updatedExams });
|
||||||
dbService.saveData({ ...data, 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);
|
const updatedExams = exams.map(e => e.id === examId ? { ...e, isDeleted: false } : e);
|
||||||
updateData({ exams: updatedExams });
|
updateData({ exams: updatedExams });
|
||||||
dbService.saveData({ ...data, 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 [dbClasses, setDbClasses] = useState<Class[]>(data.classes || []);
|
||||||
const [dbCourses, setDbCourses] = useState<any[]>(data.courses || []);
|
const [dbCourses, setDbCourses] = useState<any[]>(data.courses || []);
|
||||||
|
const [dbExams, setDbExams] = useState<any[]>(data.exams || []);
|
||||||
|
|
||||||
const loadClassesAndCourses = async () => {
|
const loadClassesAndCourses = async () => {
|
||||||
try {
|
try {
|
||||||
const [clsRes, crsRes] = await Promise.all([
|
const [clsRes, crsRes, examsRes] = await Promise.all([
|
||||||
fetch('/api/turmas'),
|
fetch('/api/turmas'),
|
||||||
fetch('/api/cursos')
|
fetch('/api/cursos'),
|
||||||
|
fetch('/api/provas')
|
||||||
]);
|
]);
|
||||||
const clsData = await clsRes.json();
|
const clsData = await clsRes.json();
|
||||||
const crsData = await crsRes.json();
|
const crsData = await crsRes.json();
|
||||||
|
const examsData = await examsRes.json();
|
||||||
|
|
||||||
if (clsData.turmas) {
|
if (clsData.turmas) {
|
||||||
setDbClasses(clsData.turmas.map((t: any) => ({
|
setDbClasses(clsData.turmas.map((t: any) => ({
|
||||||
|
|
@ -63,8 +66,19 @@ const ReportCard: React.FC<ReportCardProps> = ({ data, updateData }) => {
|
||||||
if (crsData.cursos) {
|
if (crsData.cursos) {
|
||||||
setDbCourses(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) {
|
} 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] = {};
|
initialGrades[subject.id] = {};
|
||||||
periods.forEach(period => {
|
periods.forEach(period => {
|
||||||
const periodGrades: any = {};
|
const periodGrades: any = {};
|
||||||
const linkedExams = (data.exams || []).filter(e =>
|
const linkedExams = (dbExams || []).filter(e =>
|
||||||
String(e.subjectId).trim() === String(subject.id).trim() &&
|
String(e.subjectId).trim() === String(subject.id).trim() &&
|
||||||
String(e.periodId).trim() === String(period.id).trim() &&
|
String(e.periodId).trim() === String(period.id).trim()
|
||||||
!!subsMap[String(e.id).trim()]
|
|
||||||
);
|
);
|
||||||
|
|
||||||
if (linkedExams.length > 0) {
|
if (linkedExams.length > 0) {
|
||||||
|
|
@ -653,8 +666,8 @@ const ReportCard: React.FC<ReportCardProps> = ({ data, updateData }) => {
|
||||||
) : (
|
) : (
|
||||||
<div className="space-y-6">
|
<div className="space-y-6">
|
||||||
{subjects.map(subject => {
|
{subjects.map(subject => {
|
||||||
// Encontrar provas vinculadas a esta disciplina
|
// Encontrar provas ativas vinculadas a esta disciplina
|
||||||
const linkedExams = (data.exams || []).filter(e => String(e.subjectId).trim() === String(subject.id).trim());
|
const linkedExams = (dbExams || []).filter(e => String(e.subjectId).trim() === String(subject.id).trim());
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div key={subject.id} className="bg-slate-50 rounded-2xl p-6 border border-slate-100 space-y-4">
|
<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>
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
{(() => {
|
{(() => {
|
||||||
const linkedExams = (data.exams || []).filter(e =>
|
const linkedExams = (dbExams || []).filter(e =>
|
||||||
String(e.subjectId).trim() === String(subject.id).trim() &&
|
String(e.subjectId).trim() === String(subject.id).trim()
|
||||||
!!studentSubmissions[String(e.id).trim()]
|
|
||||||
);
|
);
|
||||||
const provasCount = linkedExams.filter(e => (e as any).evaluationType !== 'activity').length;
|
const provasCount = linkedExams.filter(e => (e as any).evaluationType !== 'activity').length;
|
||||||
const atividadesCount = 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>
|
||||||
<div className="flex flex-col gap-6">
|
<div className="flex flex-col gap-6">
|
||||||
{periods.map(period => {
|
{periods.map(period => {
|
||||||
const linkedExams = (data.exams || []).filter(e =>
|
const linkedExams = (dbExams || []).filter(e =>
|
||||||
String(e.subjectId).trim() === String(subject.id).trim() &&
|
String(e.subjectId).trim() === String(subject.id).trim() &&
|
||||||
String(e.periodId).trim() === String(period.id).trim() &&
|
String(e.periodId).trim() === String(period.id).trim()
|
||||||
!!studentSubmissions[String(e.id).trim()]
|
|
||||||
);
|
);
|
||||||
const periodGrades = studentGrades[subject.id]?.[period.id] || {};
|
const periodGrades = studentGrades[subject.id]?.[period.id] || {};
|
||||||
const validPeriodValues = Object.values(periodGrades).filter(v => v !== '');
|
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() {
|
export async function getFuncionarios() {
|
||||||
const { rows } = await pool.query('SELECT * FROM funcionarios ORDER BY nome ASC');
|
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() {
|
export async function getCategoriasFuncionarios() {
|
||||||
const { rows } = await pool.query('SELECT * FROM categorias_funcionarios ORDER BY nome ASC');
|
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) {
|
export async function insertFuncionario(f) {
|
||||||
|
|
@ -609,7 +622,16 @@ export async function deleteCategoriaFuncionario(id) {
|
||||||
// ============================================================
|
// ============================================================
|
||||||
export async function getAlunos() {
|
export async function getAlunos() {
|
||||||
const result = await pool.query("SELECT * FROM alunos ORDER BY nome ASC");
|
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) {
|
export async function insertAluno(a) {
|
||||||
|
|
@ -775,8 +797,36 @@ export async function deleteAulas(ids) {
|
||||||
// PROVAS & QUESTÕES (FASE 5)
|
// PROVAS & QUESTÕES (FASE 5)
|
||||||
// ============================================================
|
// ============================================================
|
||||||
export async function getProvas() {
|
export async function getProvas() {
|
||||||
const result = await pool.query('SELECT * FROM provas ORDER BY created_at DESC');
|
const { rows: provasRows } = await pool.query('SELECT * FROM provas ORDER BY created_at DESC');
|
||||||
return result.rows;
|
|
||||||
|
// 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) {
|
export async function getQuestoesDaProva(provaId) {
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue