Fix jwt-token
This commit is contained in:
@@ -16,6 +16,7 @@ const Devices = () => {
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [showAddModal, setShowAddModal] = useState(false);
|
||||
const [editingDevice, setEditingDevice] = useState(null);
|
||||
const [filter, setFilter] = useState('all'); // 'all', 'approved', 'pending'
|
||||
|
||||
useEffect(() => {
|
||||
fetchDevices();
|
||||
@@ -42,6 +43,26 @@ const Devices = () => {
|
||||
setShowAddModal(true);
|
||||
};
|
||||
|
||||
const handleApproveDevice = async (deviceId) => {
|
||||
try {
|
||||
await api.post(`/devices/${deviceId}/approve`);
|
||||
fetchDevices();
|
||||
} catch (error) {
|
||||
console.error('Error approving device:', error);
|
||||
}
|
||||
};
|
||||
|
||||
const handleRejectDevice = async (deviceId) => {
|
||||
if (window.confirm('Are you sure you want to reject this device?')) {
|
||||
try {
|
||||
await api.post(`/devices/${deviceId}/reject`);
|
||||
fetchDevices();
|
||||
} catch (error) {
|
||||
console.error('Error rejecting device:', error);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const handleDeleteDevice = async (deviceId) => {
|
||||
if (window.confirm('Are you sure you want to deactivate this device?')) {
|
||||
try {
|
||||
@@ -74,6 +95,14 @@ const Devices = () => {
|
||||
return 'Lost';
|
||||
};
|
||||
|
||||
const filteredDevices = devices.filter(device => {
|
||||
if (filter === 'approved') return device.is_approved;
|
||||
if (filter === 'pending') return !device.is_approved;
|
||||
return true; // 'all'
|
||||
});
|
||||
|
||||
const pendingCount = devices.filter(device => !device.is_approved).length;
|
||||
|
||||
if (loading) {
|
||||
return (
|
||||
<div className="flex items-center justify-center h-64">
|
||||
@@ -91,6 +120,11 @@ const Devices = () => {
|
||||
</h3>
|
||||
<p className="mt-1 text-sm text-gray-500">
|
||||
Manage your drone detection devices
|
||||
{pendingCount > 0 && (
|
||||
<span className="ml-2 px-2 py-1 text-xs font-medium bg-yellow-200 text-yellow-800 rounded-full">
|
||||
{pendingCount} pending approval
|
||||
</span>
|
||||
)}
|
||||
</p>
|
||||
</div>
|
||||
<button
|
||||
@@ -102,10 +136,51 @@ const Devices = () => {
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{/* Filter Tabs */}
|
||||
<div className="border-b border-gray-200">
|
||||
<nav className="-mb-px flex space-x-8">
|
||||
<button
|
||||
onClick={() => setFilter('all')}
|
||||
className={`py-2 px-1 border-b-2 font-medium text-sm ${
|
||||
filter === 'all'
|
||||
? 'border-primary-500 text-primary-600'
|
||||
: 'border-transparent text-gray-500 hover:text-gray-700 hover:border-gray-300'
|
||||
}`}
|
||||
>
|
||||
All Devices ({devices.length})
|
||||
</button>
|
||||
<button
|
||||
onClick={() => setFilter('approved')}
|
||||
className={`py-2 px-1 border-b-2 font-medium text-sm ${
|
||||
filter === 'approved'
|
||||
? 'border-primary-500 text-primary-600'
|
||||
: 'border-transparent text-gray-500 hover:text-gray-700 hover:border-gray-300'
|
||||
}`}
|
||||
>
|
||||
Approved ({devices.filter(d => d.is_approved).length})
|
||||
</button>
|
||||
<button
|
||||
onClick={() => setFilter('pending')}
|
||||
className={`py-2 px-1 border-b-2 font-medium text-sm relative ${
|
||||
filter === 'pending'
|
||||
? 'border-primary-500 text-primary-600'
|
||||
: 'border-transparent text-gray-500 hover:text-gray-700 hover:border-gray-300'
|
||||
}`}
|
||||
>
|
||||
Pending Approval ({pendingCount})
|
||||
{pendingCount > 0 && (
|
||||
<span className="absolute -top-1 -right-1 h-2 w-2 bg-yellow-400 rounded-full"></span>
|
||||
)}
|
||||
</button>
|
||||
</nav>
|
||||
</div>
|
||||
|
||||
{/* Device Grid */}
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
|
||||
{devices.map((device) => (
|
||||
<div key={device.id} className="bg-white rounded-lg shadow border border-gray-200 hover:shadow-md transition-shadow">
|
||||
{filteredDevices.map((device) => (
|
||||
<div key={device.id} className={`bg-white rounded-lg shadow border hover:shadow-md transition-shadow ${
|
||||
!device.is_approved ? 'border-yellow-300 bg-yellow-50' : 'border-gray-200'
|
||||
}`}>
|
||||
<div className="p-6">
|
||||
<div className="flex items-center justify-between mb-4">
|
||||
<div className="flex items-center space-x-3">
|
||||
@@ -115,6 +190,11 @@ const Devices = () => {
|
||||
<h4 className="text-lg font-medium text-gray-900">
|
||||
{device.name || `Device ${device.id}`}
|
||||
</h4>
|
||||
{!device.is_approved && (
|
||||
<span className="px-2 py-1 text-xs font-medium bg-yellow-200 text-yellow-800 rounded-full">
|
||||
Needs Approval
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
<div className="flex space-x-1">
|
||||
<button
|
||||
@@ -142,6 +222,15 @@ const Devices = () => {
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div className="flex items-center justify-between">
|
||||
<span className="text-sm text-gray-500">Approval</span>
|
||||
<span className={`px-2 py-1 rounded-full text-xs font-medium ${
|
||||
device.is_approved ? 'bg-green-100 text-green-800' : 'bg-yellow-100 text-yellow-800'
|
||||
}`}>
|
||||
{device.is_approved ? 'Approved' : 'Pending'}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div className="flex items-center justify-between">
|
||||
<span className="text-sm text-gray-500">Device ID</span>
|
||||
<span className="text-sm font-medium text-gray-900">
|
||||
@@ -206,6 +295,22 @@ const Devices = () => {
|
||||
|
||||
{/* Device Actions */}
|
||||
<div className="mt-4 pt-4 border-t border-gray-200">
|
||||
{!device.is_approved ? (
|
||||
<div className="flex space-x-2 mb-2">
|
||||
<button
|
||||
onClick={() => handleApproveDevice(device.id)}
|
||||
className="flex-1 text-xs bg-green-100 text-green-700 py-2 px-3 rounded hover:bg-green-200 transition-colors font-medium"
|
||||
>
|
||||
✓ Approve Device
|
||||
</button>
|
||||
<button
|
||||
onClick={() => handleRejectDevice(device.id)}
|
||||
className="flex-1 text-xs bg-red-100 text-red-700 py-2 px-3 rounded hover:bg-red-200 transition-colors font-medium"
|
||||
>
|
||||
✗ Reject
|
||||
</button>
|
||||
</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">
|
||||
View Details
|
||||
@@ -220,6 +325,23 @@ const Devices = () => {
|
||||
))}
|
||||
</div>
|
||||
|
||||
{filteredDevices.length === 0 && devices.length > 0 && (
|
||||
<div className="text-center py-12">
|
||||
<ServerIcon className="mx-auto h-12 w-12 text-gray-400" />
|
||||
<h3 className="mt-2 text-sm font-medium text-gray-900">
|
||||
No {filter === 'all' ? '' : filter} devices
|
||||
</h3>
|
||||
<p className="mt-1 text-sm text-gray-500">
|
||||
{filter === 'pending'
|
||||
? 'No devices are currently pending approval.'
|
||||
: filter === 'approved'
|
||||
? 'No devices have been approved yet.'
|
||||
: 'No devices match the current filter.'
|
||||
}
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{devices.length === 0 && (
|
||||
<div className="text-center py-12">
|
||||
<ServerIcon className="mx-auto h-12 w-12 text-gray-400" />
|
||||
|
||||
Reference in New Issue
Block a user