diff --git a/backend/src/index.js b/backend/src/index.js index 991c85f..e32061f 100644 --- a/backend/src/index.js +++ b/backend/src/index.js @@ -31,11 +31,11 @@ app.use('/api/auth', authRoutes); // Plates CRUD app.get('/api/plates', authenticateToken, async (req, res) => { try { - // Users see their own plates? Or all? - // Requirement: "usuarios al agregar nuevas patentes, deberan ser permitidas por el administrador" - // Let's users see all but maybe status distinguishes them. - // For now, let's return all. + // Filter based on role + const where = req.user.role === 'ADMIN' ? {} : { addedById: req.user.id }; + const plates = await prisma.plate.findMany({ + where, include: { addedBy: { select: { username: true } } } }); res.json(plates); diff --git a/frontend/src/pages/AdminDashboard.jsx b/frontend/src/pages/AdminDashboard.jsx index da5527c..4a3fd32 100644 --- a/frontend/src/pages/AdminDashboard.jsx +++ b/frontend/src/pages/AdminDashboard.jsx @@ -1,28 +1,64 @@ import { useState, useEffect } from 'react'; import axios from 'axios'; -import { Users, CheckCircle, XCircle, Shield, Trash2 } from 'lucide-react'; +import io from 'socket.io-client'; +import { Users, CheckCircle, XCircle, Shield, Trash2, Camera, Clock, Calendar } from 'lucide-react'; const API_URL = import.meta.env.VITE_API_URL || 'http://localhost:3000'; +const socket = io(API_URL); function AdminDashboard({ token }) { const [users, setUsers] = useState([]); const [plates, setPlates] = useState([]); const [newUser, setNewUser] = useState({ username: '', password: '', role: 'USER' }); - const [activeTab, setActiveTab] = useState('plates'); // 'plates' | 'users' + const [activeTab, setActiveTab] = useState('monitor'); // 'monitor' | 'plates' | 'users' + + // Monitor State + const [detections, setDetections] = useState([]); + const [historyLogs, setHistoryLogs] = useState([]); + const [viewMode, setViewMode] = useState('live'); // 'live' | 'history' + const [selectedDate, setSelectedDate] = useState(new Date().toISOString().split('T')[0]); useEffect(() => { fetchData(); + + // Live detection listener + socket.on('new_detection', (data) => { + setDetections(prev => [data, ...prev].slice(0, 10)); + }); + + return () => socket.off('new_detection'); }, [token]); + useEffect(() => { + if (viewMode === 'history' && activeTab === 'monitor') { + fetchHistory(selectedDate); + } + }, [viewMode, selectedDate, activeTab]); + const fetchData = async () => { try { const authHeader = { headers: { Authorization: `Bearer ${token}` } }; - const [usersRes, platesRes] = await Promise.all([ + const [usersRes, platesRes, recentRes] = await Promise.all([ axios.get(`${API_URL}/api/auth`, authHeader).catch(err => ({ data: [] })), - axios.get(`${API_URL}/api/plates`, authHeader) + axios.get(`${API_URL}/api/plates`, authHeader), + axios.get(`${API_URL}/api/recent`, authHeader) ]); setUsers(usersRes.data); setPlates(platesRes.data); + setDetections(recentRes.data.map(log => ({ + plate: log.plateNumber, + status: log.accessStatus, + timestamp: log.timestamp + }))); + } catch (err) { + console.error(err); + } + }; + + const fetchHistory = async (date) => { + try { + const res = await axios.get(`${API_URL}/api/history?date=${date}`); + setHistoryLogs(res.data); } catch (err) { console.error(err); } @@ -65,6 +101,15 @@ function AdminDashboard({ token }) { } }; + const StatusBadge = ({ status }) => ( + + {status} + + ); + return (
No logs found for this date.
} +No plates registered.
} +No plates registered.
} {plates.map(plate => (