diff --git a/backend/src/index.js b/backend/src/index.js index 1bd7ad1..766ffa1 100644 --- a/backend/src/index.js +++ b/backend/src/index.js @@ -94,16 +94,49 @@ app.put('/api/plates/:id/approve', authenticateToken, isAdmin, async (req, res) }); // Admin: Delete Plate (Optional but good to have) -app.delete('/api/plates/:id', authenticateToken, isAdmin, async (req, res) => { +// Delete Plate (Admin or Owner) +app.delete('/api/plates/:id', authenticateToken, async (req, res) => { const { id } = req.params; try { + const plate = await prisma.plate.findUnique({ where: { id: parseInt(id) } }); + if (!plate) return res.status(404).json({ error: 'Plate not found' }); + + // Check permissions + if (req.user.role !== 'ADMIN' && plate.addedById !== req.user.id) { + return res.status(403).json({ error: 'Unauthorized' }); + } + await prisma.plate.delete({ where: { id: parseInt(id) } }); + + io.emit('plate_deleted', { id: parseInt(id) }); + res.json({ message: 'Plate deleted' }); } catch (err) { res.status(500).json({ error: err.message }); } }); +// Delete Person (Admin or Owner) +app.delete('/api/people/:id', authenticateToken, async (req, res) => { + const { id } = req.params; + try { + const person = await prisma.person.findUnique({ where: { id: parseInt(id) } }); + if (!person) return res.status(404).json({ error: 'Person not found' }); + + if (req.user.role !== 'ADMIN' && person.addedById !== req.user.id) { + return res.status(403).json({ error: 'Unauthorized' }); + } + + await prisma.person.delete({ where: { id: parseInt(id) } }); + + io.emit('person_deleted', { id: parseInt(id) }); + + res.json({ message: 'Person deleted' }); + } catch (err) { + res.status(500).json({ error: err.message }); + } +}); + // History Endpoint app.get('/api/history', async (req, res) => { const { date } = req.query; // Format: YYYY-MM-DD diff --git a/frontend/src/pages/AdminDashboard.jsx b/frontend/src/pages/AdminDashboard.jsx index 35d5f3b..4c8f6a4 100644 --- a/frontend/src/pages/AdminDashboard.jsx +++ b/frontend/src/pages/AdminDashboard.jsx @@ -34,11 +34,17 @@ function AdminDashboard({ token }) { // Real-time updates for approvals socket.on('new_plate_registered', () => fetchData()); socket.on('new_person_registered', () => fetchData()); + socket.on('plate_status_updated', () => fetchData()); // Reused for consistency + socket.on('plate_deleted', () => fetchData()); + socket.on('person_deleted', () => fetchData()); return () => { socket.off('new_detection'); socket.off('new_plate_registered'); socket.off('new_person_registered'); + socket.off('plate_status_updated'); + socket.off('plate_deleted'); + socket.off('person_deleted'); }; }, [token]); diff --git a/frontend/src/pages/UserDashboard.jsx b/frontend/src/pages/UserDashboard.jsx index bec0974..f5a83f1 100644 --- a/frontend/src/pages/UserDashboard.jsx +++ b/frontend/src/pages/UserDashboard.jsx @@ -1,6 +1,6 @@ import { useState, useEffect } from 'react'; import axios from 'axios'; -import { Car, Clock, CheckCircle, AlertCircle, Users } from 'lucide-react'; +import { Car, Clock, CheckCircle, AlertCircle, Users, Trash2, PlusCircle, UserPlus, AlertTriangle } from 'lucide-react'; import io from 'socket.io-client'; const API_URL = import.meta.env.VITE_API_URL || 'http://localhost:3000'; @@ -76,7 +76,7 @@ function UserDashboard({ token, username }) { } }; - const handleRegisterPerson = async (e) => { + const handleAddPerson = async (e) => { e.preventDefault(); try { await axios.post(`${API_URL}/api/people`, newPerson, { @@ -90,9 +90,33 @@ function UserDashboard({ token, username }) { } }; + const handleDeletePlate = async (id) => { + if (!confirm('Are you sure you want to delete this plate?')) return; + try { + await axios.delete(`${API_URL}/api/plates/${id}`, { + headers: { Authorization: `Bearer ${token}` } + }); + fetchPlates(); + } catch (err) { + alert(err.response?.data?.error || err.message); + } + }; + + const handleDeletePerson = async (id) => { + if (!confirm('Are you sure you want to delete this visitor?')) return; + try { + await axios.delete(`${API_URL}/api/people/${id}`, { + headers: { Authorization: `Bearer ${token}` } + }); + fetchPeople(); + } catch (err) { + alert(err.response?.data?.error || err.message); + } + }; + return (
Manage your vehicles and visitors.
No plates registered.
} {plates.map(plate => ( -No visitors registered.
} {people.map(p => ( -