diff --git a/backend/src/index.js b/backend/src/index.js index 576bf56..546b5da 100644 --- a/backend/src/index.js +++ b/backend/src/index.js @@ -13,7 +13,7 @@ app.use(express.json()); const server = http.createServer(app); const io = new Server(server, { cors: { - origin: "*", + origin: "*", methods: ["GET", "POST"] } }); @@ -45,11 +45,42 @@ app.post('/api/plates', async (req, res) => { } }); +// History Endpoint +app.get('/api/history', async (req, res) => { + const { date } = req.query; // Format: YYYY-MM-DD + if (!date) { + return res.status(400).json({ error: 'Date is required' }); + } + + const startDate = new Date(date); + startDate.setHours(0, 0, 0, 0); + + const endDate = new Date(date); + endDate.setHours(23, 59, 59, 999); + + try { + const logs = await prisma.accessLog.findMany({ + where: { + timestamp: { + gte: startDate, + lte: endDate + } + }, + orderBy: { + timestamp: 'desc' + } + }); + res.json(logs); + } catch (err) { + res.status(500).json({ error: err.message }); + } +}); + // Detection Endpoint (from Python) app.post('/api/detect', async (req, res) => { const { plate_number } = req.body; console.log(`Detected: ${plate_number}`); - + try { // Check if plate exists let plate = await prisma.plate.findUnique({ @@ -57,15 +88,15 @@ app.post('/api/detect', async (req, res) => { }); let accessStatus = 'DENIED'; - + if (plate && plate.status === 'ALLOWED') { accessStatus = 'GRANTED'; - } + } if (!plate) { - // Optional: Auto-create unknown plates? - // For now, treat as UNKNOWN (Denied) - accessStatus = 'UNKNOWN'; + // Optional: Auto-create unknown plates? + // For now, treat as UNKNOWN (Denied) + accessStatus = 'UNKNOWN'; } // Log the access attempt diff --git a/frontend/src/App.jsx b/frontend/src/App.jsx index abdcc52..6d27f0d 100644 --- a/frontend/src/App.jsx +++ b/frontend/src/App.jsx @@ -1,7 +1,7 @@ import { useState, useEffect } from 'react' import io from 'socket.io-client' import axios from 'axios' -import { Car, AlertCircle, CheckCircle, XCircle, Clock } from 'lucide-react' +import { Car, AlertCircle, CheckCircle, XCircle, Clock, Calendar } from 'lucide-react' // Env var logic for Vite const API_URL = import.meta.env.VITE_API_URL || 'http://localhost:3000'; @@ -14,6 +14,12 @@ function App() { const [showModal, setShowModal] = useState(false); const [newPlate, setNewPlate] = useState({ number: '', owner: '' }); + // History State + const [viewMode, setViewMode] = useState('live'); // 'live' | 'history' + const [selectedDate, setSelectedDate] = useState(new Date().toISOString().split('T')[0]); + const [historyLogs, setHistoryLogs] = useState([]); + const [loadingHistory, setLoadingHistory] = useState(false); + const handleRegister = async (e) => { e.preventDefault(); try { @@ -56,6 +62,26 @@ function App() { } }; + const fetchHistory = async (date) => { + setLoadingHistory(true); + try { + const res = await axios.get(`${API_URL}/api/history?date=${date}`); + setHistoryLogs(res.data); + } catch (err) { + console.error("Error fetching history:", err); + // alert("Failed to fetch history"); + } finally { + setLoadingHistory(false); + } + }; + + // Fetch history when date changes or when switching to history view + useEffect(() => { + if (viewMode === 'history') { + fetchHistory(selectedDate); + } + }, [viewMode, selectedDate]); + const StatusBadge = ({ status }) => { const colors = { GRANTED: 'bg-green-500/20 text-green-400 border-green-500/50', @@ -142,58 +168,123 @@ function App() {
Camera Offline or Connecting...
-No detections yet...
-Camera Offline or Connecting...
No detections yet...
+Loading history...
+ ) : historyLogs.length === 0 ? ( +