fix: corrige inconsistências críticas de backend e navegação

- Backend: substitui string literals com aspas duplas por aspas simples
  nas queries PostgreSQL (settings, GTO builder, google_tokens delete)
- Backend: corrige alias SQL 'dentistanome' para 'dentistaNome' (quoted)
  e ajusta frontend para usar 'pacientenome' (lowercase do PG)
- Backend: move app.listen() para após todas as rotas registradas
- Backend: ativa generateIntelligentNotifications no startup + intervalo 5min
- Backend: magic link usa process.env.APP_URL em vez de localhost:3000 hardcoded
- Frontend: adiciona 'relatorios' às permissões do role 'funcionario'
- Frontend: adiciona onClick ao botão Relatórios do Dashboard
- Frontend: adiciona Content-Type e JWT token em salvarAgendamento/atualizarAgendamento
- Frontend: MeusTratamentos passa JWT token nas chamadas a /api/guias
- Frontend: ClinicasView passa JWT token no endpoint de atualização de cor
- Frontend: corrige subtítulo 'MYSQL + GOOGLE' para 'POSTGRESQL + GOOGLE'

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
VPS 4 Builder
2026-05-13 21:43:23 +02:00
parent 34129d4528
commit 1330147eaf
8 changed files with 44 additions and 21 deletions
+21 -12
View File
@@ -262,8 +262,6 @@ const createOAuth2Client = () => new google.auth.OAuth2(
process.env.GOOGLE_REDIRECT_URI || `http://localhost:${PORT}/api/oauth2callback`
);
app.listen(PORT, '0.0.0.0', () => console.log(`[SERVER] Running on http://0.0.0.0:${PORT}`));
// --- GOOGLE AUTH ROUTES ---
app.get('/api/auth/google/url', (req, res) => {
const { dentistId } = req.query;
@@ -321,7 +319,7 @@ app.get('/api/auth/google/status', async (req, res) => {
app.delete('/api/auth/google/:ownerId', async (req, res) => {
try {
await pool.query('DELETE FROM google_tokens WHERE owner_id = ?', [req.params.ownerId]);
await pool.query('DELETE FROM google_tokens WHERE owner_id = $1', [req.params.ownerId]);
res.json({ success: true });
} catch (err) { res.status(500).json({ error: err.message }); }
});
@@ -329,7 +327,7 @@ app.delete('/api/auth/google/:ownerId', async (req, res) => {
// --- SETTINGS ---
app.get('/api/settings', async (req, res) => {
try {
const [rows] = await pool.query('SELECT * FROM settings WHERE id = "main"');
const { rows } = await pool.query("SELECT * FROM settings WHERE id = 'main'");
res.json(rows[0] ? JSON.parse(rows[0].data) : {});
} catch (err) { res.status(500).json({ error: err.message }); }
});
@@ -337,7 +335,11 @@ app.get('/api/settings', async (req, res) => {
app.post('/api/settings', async (req, res) => {
try {
const data = JSON.stringify(req.body);
await pool.query('INSERT INTO settings (id, category, data) VALUES ("main", "general", ?) ON DUPLICATE KEY UPDATE data = ?', [data, data]);
await pool.query(
`INSERT INTO settings (id, category, data) VALUES ('main', 'general', $1)
ON CONFLICT (id) DO UPDATE SET data = EXCLUDED.data`,
[data]
);
res.json({ success: true });
} catch (err) { res.status(500).json({ error: err.message }); }
});
@@ -347,8 +349,8 @@ app.post('/api/dentistas/magic-link', async (req, res) => {
const { clinicaId, dentistName } = req.body;
try {
const token = Math.random().toString(36).substring(2, 15);
// Em um sistema real, salvaríamos esse token em uma tabela de convites
const magicLink = `http://localhost:3000/#/cadastro-dentista?token=${token}&clinica=${clinicaId}&nome=${encodeURIComponent(dentistName)}`;
const appUrl = process.env.APP_URL || `http://localhost:${process.env.FRONTEND_PORT || 3000}`;
const magicLink = `${appUrl}/#/cadastro-dentista?token=${token}&clinica=${clinicaId}&nome=${encodeURIComponent(dentistName)}`;
res.json({ success: true, link: magicLink });
} catch (err) { res.status(500).json({ error: err.message }); }
});
@@ -437,7 +439,7 @@ app.get('/api/google/events', async (req, res) => {
}
// 2. Legacy Support: Check Settings for Public Calendars (API KEY)
const [rows] = await pool.query('SELECT data FROM settings WHERE id = "main"');
const [rows] = await pool.query("SELECT data FROM settings WHERE id = 'main'");
if (rows[0]) {
const settings = JSON.parse(rows[0].data);
const apiKey = settings.googleApiKey;
@@ -513,7 +515,8 @@ app.get('/api/dashboard/stats', async (req, res) => {
// Recent appointments with patient and dentist names
const { rows: recent } = await pool.query(`
SELECT a.*, d.nome as dentistanome
SELECT a.id, a.pacientenome, a.start_time, a.status,
d.nome as "dentistaNome"
FROM agendamentos a
LEFT JOIN dentistas d ON a.dentistaid = d.id
ORDER BY a.start_time DESC
@@ -1159,7 +1162,7 @@ app.post('/api/gto-builder', async (req, res) => {
const { pacienteId, dentistaId, credenciadaId } = req.body;
const id = Math.random().toString(36).substring(2, 9).toUpperCase();
await pool.query(
'INSERT INTO gto_builders (id, pacienteId, dentistaId, credenciadaId, status, total) VALUES (?, ?, ?, ?, "RASCUNHO", 0)',
"INSERT INTO gto_builders (id, pacienteId, dentistaId, credenciadaId, status, total) VALUES ($1, $2, $3, $4, 'RASCUNHO', 0)",
[id, pacienteId, dentistaId, credenciadaId]
);
res.json({ id });
@@ -1204,7 +1207,7 @@ app.delete('/api/gto-builder/:id/item/:itemId', async (req, res) => {
app.put('/api/gto-builder/:id/finalizar', async (req, res) => {
try {
await pool.query('UPDATE gto_builders SET status = "FINALIZADA" WHERE id = ?', [req.params.id]);
await pool.query("UPDATE gto_builders SET status = 'FINALIZADA' WHERE id = $1", [req.params.id]);
// Logic to migrate to guias_odontologicas would go here in a real app
// For now, we just mark as finished.
@@ -1297,5 +1300,11 @@ async function generateIntelligentNotifications() {
}
}
// Start server after all routes are registered
app.listen(PORT, '0.0.0.0', () => {
console.log(`[SERVER] Running on http://0.0.0.0:${PORT}`);
// Run immediately on startup, then every 5 minutes
generateIntelligentNotifications();
setInterval(generateIntelligentNotifications, 5 * 60 * 1000);
});