From d6f90c19f18410c12d890068926d163c885ea0cf Mon Sep 17 00:00:00 2001 From: raven Date: Sun, 28 Dec 2025 21:56:31 -0300 Subject: [PATCH] Add from user rm aproved/denied plates/rut --- backend/src/index.js | 35 ++++- frontend/src/pages/AdminDashboard.jsx | 6 + frontend/src/pages/UserDashboard.jsx | 194 ++++++++++++++++++-------- 3 files changed, 177 insertions(+), 58 deletions(-) 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 (
-
+

@@ -101,81 +125,137 @@ function UserDashboard({ token, username }) {

Manage your vehicles and visitors.

- {/* Plates Section */} -
-

- My Registered Plates -

+
+ {/* Plate Registration Form (Existing) */} +
+

+ Register New Plate +

+
+ setNewPlate({ ...newPlate, number: e.target.value.toUpperCase() })} + required + /> + setNewPlate({ ...newPlate, owner: e.target.value })} + required + /> + +
+
-
+ {/* Visitor Registration Form (Existing) */} +
+

+ Register Visitor +

+
+
+ setNewPerson({ ...newPerson, rut: e.target.value })} + required + /> + +
+ setNewPerson({ ...newPerson, name: e.target.value })} + required + /> + +
+
+
+ + {/* My Plates List */} +
+

My Registered Plates

+
{plates.length === 0 &&

No plates registered.

} {plates.map(plate => ( -
+
{plate.number}
{plate.owner}
- - {plate.status} - +
+ {plate.status === 'ALLOWED' && ( + + ACTIVE + + )} + {plate.status === 'PENDING' && ( + + PENDING + + )} + {plate.status === 'DENIED' && ( + + DENIED + + )} + +
))}
- -
- setNewPlate({ ...newPlate, number: e.target.value })} - required - /> - setNewPlate({ ...newPlate, owner: e.target.value })} - required - /> - -
- {/* Visitors Section */} -
-

- My Visitors -

-
+ {/* My Visitors List */} +
+

My Visitors

+
{people.length === 0 &&

No visitors registered.

} {people.map(p => ( -
-
+
+
{p.name}
- +
{p.rut}
+
Until: {new Date(p.endDate).toLocaleDateString()}
+
+
+ {p.status} -
-
{p.rut}
-
- Until: {new Date(p.endDate).toLocaleDateString()} +
))}
- -
- setNewPerson({ ...newPerson, name: e.target.value })} required /> - setNewPerson({ ...newPerson, rut: e.target.value })} required /> - - -