186 lines
5.6 KiB
JavaScript
186 lines
5.6 KiB
JavaScript
|
|
const express = require('express');
|
|
const cors = require('cors');
|
|
const path = require('path');
|
|
const pool = require('./db');
|
|
|
|
const app = express();
|
|
const PORT = process.env.PORT || 3001; // Porta do backend
|
|
|
|
app.use(cors()); // Permite que o React (localhost:3000) acesse este servidor
|
|
app.use(express.json());
|
|
|
|
// Serve static files from the React app
|
|
if (process.env.NODE_ENV === 'production') {
|
|
app.use(express.static(path.join(__dirname, '../dist')));
|
|
}
|
|
|
|
// --- Rotas de Usuários ---
|
|
|
|
// Listar Usuários (com filtro opcional de tenant)
|
|
app.get('/api/users', async (req, res) => {
|
|
try {
|
|
const { tenantId } = req.query;
|
|
let query = 'SELECT * FROM users';
|
|
const params = [];
|
|
|
|
if (tenantId && tenantId !== 'all') {
|
|
query += ' WHERE tenant_id = ?';
|
|
params.push(tenantId);
|
|
}
|
|
|
|
const [rows] = await pool.query(query, params);
|
|
res.json(rows);
|
|
} catch (error) {
|
|
console.error('Erro ao buscar usuários:', error);
|
|
res.status(500).json({ error: error.message });
|
|
}
|
|
});
|
|
|
|
// Detalhe do Usuário
|
|
app.get('/api/users/:id', async (req, res) => {
|
|
try {
|
|
const [rows] = await pool.query('SELECT * FROM users WHERE id = ?', [req.params.id]);
|
|
if (rows.length === 0) return res.status(404).json({ message: 'User not found' });
|
|
res.json(rows[0]);
|
|
} catch (error) {
|
|
res.status(500).json({ error: error.message });
|
|
}
|
|
});
|
|
|
|
// --- Rotas de Atendimentos ---
|
|
|
|
// Listar Atendimentos (Dashboard)
|
|
app.get('/api/attendances', async (req, res) => {
|
|
try {
|
|
const { tenantId, userId, teamId, startDate, endDate } = req.query;
|
|
|
|
let query = `
|
|
SELECT a.*, u.team_id
|
|
FROM attendances a
|
|
JOIN users u ON a.user_id = u.id
|
|
WHERE a.tenant_id = ?
|
|
`;
|
|
const params = [tenantId];
|
|
|
|
// Filtro de Data
|
|
if (startDate && endDate) {
|
|
query += ' AND a.created_at BETWEEN ? AND ?';
|
|
params.push(new Date(startDate), new Date(endDate));
|
|
}
|
|
|
|
// Filtro de Usuário
|
|
if (userId && userId !== 'all') {
|
|
query += ' AND a.user_id = ?';
|
|
params.push(userId);
|
|
}
|
|
|
|
// Filtro de Time (baseado na tabela users ou teams)
|
|
if (teamId && teamId !== 'all') {
|
|
query += ' AND u.team_id = ?';
|
|
params.push(teamId);
|
|
}
|
|
|
|
query += ' ORDER BY a.created_at DESC';
|
|
|
|
const [rows] = await pool.query(query, params);
|
|
|
|
// Tratamento de campos JSON se o MySQL retornar como string
|
|
const processedRows = rows.map(row => ({
|
|
...row,
|
|
attention_points: typeof row.attention_points === 'string' ? JSON.parse(row.attention_points) : row.attention_points,
|
|
improvement_points: typeof row.improvement_points === 'string' ? JSON.parse(row.improvement_points) : row.improvement_points,
|
|
converted: Boolean(row.converted) // Garantir booleano
|
|
}));
|
|
|
|
res.json(processedRows);
|
|
} catch (error) {
|
|
console.error('Erro ao buscar atendimentos:', error);
|
|
res.status(500).json({ error: error.message });
|
|
}
|
|
});
|
|
|
|
// Detalhe do Atendimento
|
|
app.get('/api/attendances/:id', async (req, res) => {
|
|
try {
|
|
const [rows] = await pool.query('SELECT * FROM attendances WHERE id = ?', [req.params.id]);
|
|
if (rows.length === 0) return res.status(404).json({ message: 'Attendance not found' });
|
|
|
|
const row = rows[0];
|
|
const processedRow = {
|
|
...row,
|
|
attention_points: typeof row.attention_points === 'string' ? JSON.parse(row.attention_points) : row.attention_points,
|
|
improvement_points: typeof row.improvement_points === 'string' ? JSON.parse(row.improvement_points) : row.improvement_points,
|
|
converted: Boolean(row.converted)
|
|
};
|
|
|
|
res.json(processedRow);
|
|
} catch (error) {
|
|
res.status(500).json({ error: error.message });
|
|
}
|
|
});
|
|
|
|
// --- Rotas de Tenants (Super Admin) ---
|
|
app.get('/api/tenants', async (req, res) => {
|
|
try {
|
|
// Buscar tenants e contar usuários/atendimentos
|
|
const query = `
|
|
SELECT t.*,
|
|
(SELECT COUNT(*) FROM users u WHERE u.tenant_id = t.id) as user_count,
|
|
(SELECT COUNT(*) FROM attendances a WHERE a.tenant_id = t.id) as attendance_count
|
|
FROM tenants t
|
|
`;
|
|
const [rows] = await pool.query(query);
|
|
res.json(rows);
|
|
} catch (error) {
|
|
res.status(500).json({ error: error.message });
|
|
}
|
|
});
|
|
|
|
// Criar Tenant
|
|
app.post('/api/tenants', async (req, res) => {
|
|
const { name, slug, admin_email, status } = req.body;
|
|
const crypto = require('crypto');
|
|
|
|
const connection = await pool.getConnection();
|
|
try {
|
|
await connection.beginTransaction();
|
|
|
|
const tenantId = `tenant_${crypto.randomUUID().split('-')[0]}`; // Simple ID generation
|
|
|
|
// 1. Criar Tenant
|
|
await connection.query(
|
|
'INSERT INTO tenants (id, name, slug, admin_email, status) VALUES (?, ?, ?, ?, ?)',
|
|
[tenantId, name, slug, admin_email, status || 'active']
|
|
);
|
|
|
|
// 2. Criar Usuário Admin Default
|
|
const userId = `u_${crypto.randomUUID().split('-')[0]}`;
|
|
await connection.query(
|
|
'INSERT INTO users (id, tenant_id, name, email, role, status) VALUES (?, ?, ?, ?, ?, ?)',
|
|
[userId, tenantId, 'Admin', admin_email, 'admin', 'active']
|
|
);
|
|
|
|
await connection.commit();
|
|
res.status(201).json({ message: 'Tenant created successfully', id: tenantId });
|
|
} catch (error) {
|
|
await connection.rollback();
|
|
console.error('Erro ao criar tenant:', error);
|
|
res.status(500).json({ error: error.message });
|
|
} finally {
|
|
connection.release();
|
|
}
|
|
});
|
|
|
|
|
|
// Serve index.html for any unknown routes (for client-side routing)
|
|
if (process.env.NODE_ENV === 'production') {
|
|
app.get('*', (req, res) => {
|
|
res.sendFile(path.join(__dirname, '../dist/index.html'));
|
|
});
|
|
}
|
|
|
|
app.listen(PORT, () => {
|
|
console.log(`🚀 Servidor Backend rodando em http://localhost:${PORT}`);
|
|
});
|