Fix jwt-token
This commit is contained in:
@@ -17,6 +17,8 @@ const Devices = () => {
|
||||
const [showAddModal, setShowAddModal] = useState(false);
|
||||
const [editingDevice, setEditingDevice] = useState(null);
|
||||
const [filter, setFilter] = useState('all'); // 'all', 'approved', 'pending'
|
||||
const [showDetailsModal, setShowDetailsModal] = useState(false);
|
||||
const [selectedDevice, setSelectedDevice] = useState(null);
|
||||
|
||||
useEffect(() => {
|
||||
fetchDevices();
|
||||
@@ -63,6 +65,21 @@ const Devices = () => {
|
||||
}
|
||||
};
|
||||
|
||||
const handleViewDetails = (device) => {
|
||||
setSelectedDevice(device);
|
||||
setShowDetailsModal(true);
|
||||
};
|
||||
|
||||
const handleViewOnMap = (device) => {
|
||||
if (device.geo_lat && device.geo_lon) {
|
||||
// Open Google Maps with the device location
|
||||
const url = `https://www.google.com/maps?q=${device.geo_lat},${device.geo_lon}&z=15`;
|
||||
window.open(url, '_blank');
|
||||
} else {
|
||||
alert('Device location coordinates are not available');
|
||||
}
|
||||
};
|
||||
|
||||
const handleDeleteDevice = async (deviceId) => {
|
||||
if (window.confirm('Are you sure you want to deactivate this device?')) {
|
||||
try {
|
||||
@@ -312,10 +329,16 @@ const Devices = () => {
|
||||
</div>
|
||||
) : null}
|
||||
<div className="flex space-x-2">
|
||||
<button className="flex-1 text-xs bg-gray-100 text-gray-700 py-2 px-3 rounded hover:bg-gray-200 transition-colors">
|
||||
<button
|
||||
onClick={() => handleViewDetails(device)}
|
||||
className="flex-1 text-xs bg-gray-100 text-gray-700 py-2 px-3 rounded hover:bg-gray-200 transition-colors"
|
||||
>
|
||||
View Details
|
||||
</button>
|
||||
<button className="flex-1 text-xs bg-primary-100 text-primary-700 py-2 px-3 rounded hover:bg-primary-200 transition-colors">
|
||||
<button
|
||||
onClick={() => handleViewOnMap(device)}
|
||||
className="flex-1 text-xs bg-primary-100 text-primary-700 py-2 px-3 rounded hover:bg-primary-200 transition-colors"
|
||||
>
|
||||
View on Map
|
||||
</button>
|
||||
</div>
|
||||
@@ -372,6 +395,133 @@ const Devices = () => {
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
|
||||
{/* Device Details Modal */}
|
||||
{showDetailsModal && selectedDevice && (
|
||||
<div className="fixed inset-0 bg-gray-600 bg-opacity-50 overflow-y-auto h-full w-full z-50">
|
||||
<div className="relative top-20 mx-auto p-5 border w-96 shadow-lg rounded-md bg-white">
|
||||
<div className="mt-3">
|
||||
<div className="flex items-center justify-between mb-4">
|
||||
<h3 className="text-lg font-medium text-gray-900">
|
||||
Device Details
|
||||
</h3>
|
||||
<button
|
||||
onClick={() => setShowDetailsModal(false)}
|
||||
className="text-gray-400 hover:text-gray-600"
|
||||
>
|
||||
✕
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div className="space-y-3 text-sm">
|
||||
<div className="flex justify-between">
|
||||
<span className="font-medium text-gray-700">Device ID:</span>
|
||||
<span className="text-gray-900">{selectedDevice.id}</span>
|
||||
</div>
|
||||
|
||||
<div className="flex justify-between">
|
||||
<span className="font-medium text-gray-700">Name:</span>
|
||||
<span className="text-gray-900">{selectedDevice.name || 'Unnamed'}</span>
|
||||
</div>
|
||||
|
||||
<div className="flex justify-between">
|
||||
<span className="font-medium text-gray-700">Status:</span>
|
||||
<span className={`px-2 py-1 rounded-full text-xs font-medium ${
|
||||
getStatusColor(selectedDevice.stats?.status)
|
||||
}`}>
|
||||
{selectedDevice.stats?.status || 'Unknown'}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div className="flex justify-between">
|
||||
<span className="font-medium text-gray-700">Approved:</span>
|
||||
<span className={`px-2 py-1 rounded-full text-xs font-medium ${
|
||||
selectedDevice.is_approved ? 'bg-green-100 text-green-800' : 'bg-yellow-100 text-yellow-800'
|
||||
}`}>
|
||||
{selectedDevice.is_approved ? 'Yes' : 'Pending'}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
{selectedDevice.location_description && (
|
||||
<div className="flex justify-between">
|
||||
<span className="font-medium text-gray-700">Location:</span>
|
||||
<span className="text-gray-900 text-right">{selectedDevice.location_description}</span>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{(selectedDevice.geo_lat && selectedDevice.geo_lon) && (
|
||||
<div className="flex justify-between">
|
||||
<span className="font-medium text-gray-700">Coordinates:</span>
|
||||
<span className="text-gray-900">{selectedDevice.geo_lat}, {selectedDevice.geo_lon}</span>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{selectedDevice.last_heartbeat && (
|
||||
<div className="flex justify-between">
|
||||
<span className="font-medium text-gray-700">Last Heartbeat:</span>
|
||||
<span className="text-gray-900">{format(new Date(selectedDevice.last_heartbeat), 'MMM dd, yyyy HH:mm')}</span>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{selectedDevice.firmware_version && (
|
||||
<div className="flex justify-between">
|
||||
<span className="font-medium text-gray-700">Firmware:</span>
|
||||
<span className="text-gray-900">{selectedDevice.firmware_version}</span>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{selectedDevice.stats && (
|
||||
<div className="flex justify-between">
|
||||
<span className="font-medium text-gray-700">Detections (24h):</span>
|
||||
<span className={`font-medium ${
|
||||
selectedDevice.stats.detections_24h > 0 ? 'text-red-600' : 'text-green-600'
|
||||
}`}>
|
||||
{selectedDevice.stats.detections_24h}
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{selectedDevice.created_at && (
|
||||
<div className="flex justify-between">
|
||||
<span className="font-medium text-gray-700">Created:</span>
|
||||
<span className="text-gray-900">{format(new Date(selectedDevice.created_at), 'MMM dd, yyyy HH:mm')}</span>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className="flex space-x-2 mt-6">
|
||||
{!selectedDevice.is_approved && (
|
||||
<button
|
||||
onClick={() => {
|
||||
handleApproveDevice(selectedDevice.id);
|
||||
setShowDetailsModal(false);
|
||||
}}
|
||||
className="flex-1 bg-green-100 text-green-700 py-2 px-3 rounded hover:bg-green-200 transition-colors text-sm font-medium"
|
||||
>
|
||||
✓ Approve
|
||||
</button>
|
||||
)}
|
||||
|
||||
{(selectedDevice.geo_lat && selectedDevice.geo_lon) && (
|
||||
<button
|
||||
onClick={() => handleViewOnMap(selectedDevice)}
|
||||
className="flex-1 bg-primary-100 text-primary-700 py-2 px-3 rounded hover:bg-primary-200 transition-colors text-sm font-medium"
|
||||
>
|
||||
View on Map
|
||||
</button>
|
||||
)}
|
||||
|
||||
<button
|
||||
onClick={() => setShowDetailsModal(false)}
|
||||
className="flex-1 bg-gray-100 text-gray-700 py-2 px-3 rounded hover:bg-gray-200 transition-colors text-sm font-medium"
|
||||
>
|
||||
Close
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user