fix: memory leak, syntax errors, typescript cleanup and grade interface
This commit is contained in:
parent
f52c5084c6
commit
74216f170d
|
|
@ -23,6 +23,11 @@
|
|||
- [x] **Controle de Refação (Retake Policy):** Adicionado botão de cadeado nos cards de Avaliações para permitir ou bloquear que alunos refaçam provas no portal (Regra 15).
|
||||
- [x] **UI de Avaliações:** Padronização dos botões de edição ("Editar Prova" vs "Editar Atividade") e adição de botão de exclusão rápida direto no card.
|
||||
- [x] **Correção de Vínculo de Notas:** Garantido que o `examId` seja sempre salvo nas notas geradas pelo Portal para preenchimento automático do Boletim Escolar no Manager.
|
||||
- [x] **Fix Memory Leak:** Removido `pool.on('error')` que estava dentro da rota `PUT /api/school-data`, acumulando listeners a cada salvamento.
|
||||
- [x] **Fix SyntaxError (Backticks):** Corrigido erro de sintaxe com backticks escapados na rota do Database Explorer que impedia o servidor de iniciar.
|
||||
- [x] **Fix Static Serving Duplicado:** Consolidada a entrega de arquivos estáticos (dist) no `manager/server.selfhosted.js`, eliminando o erro 404 em produção.
|
||||
- [x] **TypeScript Cleanup:** Corrigidos erros de tipo `unknown` nos `reduce()` do ReportCard.tsx e removida função órfã `closeModal` do Settings.tsx.
|
||||
- [x] **Interface Grade Tipada:** Adicionado `examId?: string` à interface `Grade` em `types.ts`, eliminando casts `as any` inseguros.
|
||||
- [ ] Próximo Passo: Iniciar testes de estresse no servidor self-hosted para submissão massiva de fotos de frequência.
|
||||
|
||||
### 💳 Módulo Financeiro (Portal do Aluno)
|
||||
|
|
|
|||
|
|
@ -136,11 +136,11 @@ const ReportCard: React.FC<ReportCardProps> = ({ data, updateData }) => {
|
|||
|
||||
if (linkedExams.length > 0) {
|
||||
linkedExams.forEach(exam => {
|
||||
const existingGrade = grades.find(g => g.studentId === student.id && g.subjectId === subject.id && g.period === period.id && (g as any).examId === exam.id);
|
||||
const existingGrade = grades.find(g => g.studentId === student.id && g.subjectId === subject.id && g.period === period.id && g.examId === exam.id);
|
||||
periodGrades[exam.id] = existingGrade ? existingGrade.value : '';
|
||||
});
|
||||
} else {
|
||||
const existingGrade = grades.find(g => g.studentId === student.id && g.subjectId === subject.id && g.period === period.id && !(g as any).examId);
|
||||
const existingGrade = grades.find(g => g.studentId === student.id && g.subjectId === subject.id && g.period === period.id && !g.examId);
|
||||
periodGrades['direct'] = existingGrade ? existingGrade.value : '';
|
||||
}
|
||||
initialGrades[subject.id][period.id] = periodGrades;
|
||||
|
|
@ -187,7 +187,7 @@ const ReportCard: React.FC<ReportCardProps> = ({ data, updateData }) => {
|
|||
const periodSums: number[] = [];
|
||||
|
||||
Object.values(subjectPeriods).forEach((examValues: any) => {
|
||||
const sum = Object.values(examValues).reduce((a: number, b: any) => a + (b !== '' ? Number(b) : 0), 0);
|
||||
const sum: number = Object.values(examValues).reduce<number>((a, b: any) => a + (b !== '' ? Number(b) : 0), 0);
|
||||
if (Object.values(examValues).some(v => v !== '')) {
|
||||
periodSums.push(sum);
|
||||
}
|
||||
|
|
@ -529,7 +529,7 @@ const ReportCard: React.FC<ReportCardProps> = ({ data, updateData }) => {
|
|||
<div className="px-3 py-1 bg-white border border-slate-200 rounded-lg text-[10px] font-black text-slate-500">
|
||||
MÉDIA: {(() => {
|
||||
const subjectGrades = studentGrades[subject.id] || {};
|
||||
const pSums = Object.values(subjectGrades).map((exVals: any) => Object.values(exVals).reduce((a: number, b: any) => a + (b !== '' ? Number(b) : 0), 0));
|
||||
const pSums: number[] = Object.values(subjectGrades).map((exVals: any) => Object.values(exVals).reduce<number>((a, b: any) => a + (b !== '' ? Number(b) : 0), 0));
|
||||
const valid = pSums.filter(s => s > 0);
|
||||
return valid.length > 0 ? (valid.reduce((a, b) => a + b, 0) / valid.length).toFixed(1) : '0.0';
|
||||
})()}
|
||||
|
|
@ -540,7 +540,7 @@ const ReportCard: React.FC<ReportCardProps> = ({ data, updateData }) => {
|
|||
{periods.map(period => {
|
||||
const linkedExams = (data.exams || []).filter(e => e.subjectId === subject.id && e.periodId === period.id && e.status === 'published');
|
||||
const periodGrades = studentGrades[subject.id]?.[period.id] || {};
|
||||
const periodSum = Object.values(periodGrades).reduce((a: number, b: any) => a + (b !== '' ? Number(b) : 0), 0);
|
||||
const periodSum: number = Object.values(periodGrades).reduce<number>((a, b: any) => a + (b !== '' ? Number(b) : 0), 0);
|
||||
|
||||
return (
|
||||
<div key={period.id} className="bg-white border border-slate-200 rounded-xl p-4 shadow-sm space-y-3 relative">
|
||||
|
|
|
|||
|
|
@ -302,15 +302,6 @@ const Settings: React.FC<SettingsProps> = ({ data, updateData, setData }) => {
|
|||
};
|
||||
|
||||
|
||||
|
||||
const closeModal = () => {
|
||||
setIsClosing(true);
|
||||
setTimeout(() => {
|
||||
setShowImportModal(false);
|
||||
setIsClosing(false);
|
||||
}, 300);
|
||||
};
|
||||
|
||||
const handleLogoUpload = async (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
const file = e.target.files?.[0];
|
||||
if (file) {
|
||||
|
|
|
|||
|
|
@ -185,10 +185,6 @@ app.put('/api/school-data', async (req, res) => {
|
|||
END $$;
|
||||
`).catch(err => console.error('[PostgreSQL] Erro ao inicializar colunas de automação:', err));
|
||||
|
||||
pool.on('error', (err) => {
|
||||
console.error('Erro inesperado no pool:', err);
|
||||
});
|
||||
|
||||
schoolData.lastUpdated = new Date().toISOString();
|
||||
await saveSchoolData(schoolData);
|
||||
res.json({ success: true });
|
||||
|
|
@ -262,7 +258,7 @@ app.get('/api/database/tables/:tableName/data', async (req, res) => {
|
|||
const result = await pool.query(query);
|
||||
res.json({ rows: result.rows, fields: result.fields.map(f => f.name) });
|
||||
} catch (error) {
|
||||
console.error(\`Erro ao buscar dados da tabela \${req.params.tableName}:\`, error);
|
||||
console.error(`Erro ao buscar dados da tabela ${req.params.tableName}:`, error);
|
||||
res.status(500).json({ error: error.message });
|
||||
}
|
||||
});
|
||||
|
|
@ -912,14 +908,6 @@ app.get('/api/alunos/:id/carne', async (req, res) => {
|
|||
// INICIALIZAÇÃO
|
||||
// ============================================================
|
||||
async function startServer() {
|
||||
const distPath = path.join(__dirname, 'dist');
|
||||
if (fs.existsSync(distPath)) {
|
||||
app.use(express.static(distPath));
|
||||
app.use((req, res, next) => req.path.startsWith('/api') ? next() : res.sendFile(path.join(distPath, 'index.html')));
|
||||
} else {
|
||||
const vite = await import('vite').then(m => m.createServer({ server: { middlewareMode: true }, appType: 'spa' }));
|
||||
app.use(vite.middlewares);
|
||||
}
|
||||
|
||||
// Disparo Manual de Inadimplência e Lembretes
|
||||
app.post('/api/disparar_cobrancas', async (req, res) => {
|
||||
|
|
@ -1029,6 +1017,28 @@ async function startServer() {
|
|||
} catch (error) { return res.status(500).json({ error: 'Erro interno.' }); }
|
||||
});
|
||||
|
||||
// ===================================================
|
||||
// SERVE FRONTEND (Final Catch-all)
|
||||
// ===================================================
|
||||
const distPath = path.join(__dirname, 'dist');
|
||||
if (fs.existsSync(distPath)) {
|
||||
app.use(express.static(distPath));
|
||||
app.use((req, res, next) => {
|
||||
if (req.path.startsWith('/api') || req.path.startsWith('/storage')) return next();
|
||||
res.sendFile(path.join(distPath, 'index.html'));
|
||||
});
|
||||
} else {
|
||||
try {
|
||||
const vite = await import('vite').then(m => m.createServer({
|
||||
server: { middlewareMode: true },
|
||||
appType: 'spa'
|
||||
}));
|
||||
app.use(vite.middlewares);
|
||||
} catch (e) {
|
||||
console.warn('Vite dev server not available and dist folder missing.');
|
||||
}
|
||||
}
|
||||
|
||||
app.listen(PORT, '0.0.0.0', () => console.log(`🚀 EduManager Self-Hosted na porta ${PORT}`));
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -194,6 +194,7 @@ export interface Grade {
|
|||
subjectId: string;
|
||||
value: number;
|
||||
period: string; // e.g., "1º Bimestre", "Final"
|
||||
examId?: string; // Vincula a nota a uma avaliação específica
|
||||
}
|
||||
|
||||
export interface Handout {
|
||||
|
|
|
|||
Loading…
Reference in New Issue