194 lines
7.7 KiB
TypeScript
194 lines
7.7 KiB
TypeScript
|
|
'use client';
|
||
|
|
|
||
|
|
import { useState, useEffect } from 'react';
|
||
|
|
import { AddigyDevice } from '@/lib/types/addigy';
|
||
|
|
|
||
|
|
export default function AddigyDevicesPage() {
|
||
|
|
const [devices, setDevices] = useState<AddigyDevice[]>([]);
|
||
|
|
const [loading, setLoading] = useState(true);
|
||
|
|
const [error, setError] = useState<string | null>(null);
|
||
|
|
const [filterOnline, setFilterOnline] = useState(false);
|
||
|
|
|
||
|
|
useEffect(() => {
|
||
|
|
fetchDevices();
|
||
|
|
}, [filterOnline]);
|
||
|
|
|
||
|
|
const fetchDevices = async () => {
|
||
|
|
setLoading(true);
|
||
|
|
setError(null);
|
||
|
|
|
||
|
|
try {
|
||
|
|
const url = filterOnline
|
||
|
|
? '/api/addigy-devices?online=true'
|
||
|
|
: '/api/addigy-devices';
|
||
|
|
|
||
|
|
const response = await fetch(url);
|
||
|
|
const result = await response.json();
|
||
|
|
|
||
|
|
if (result.success) {
|
||
|
|
setDevices(result.data);
|
||
|
|
} else {
|
||
|
|
setError(result.error || 'Failed to fetch devices');
|
||
|
|
}
|
||
|
|
} catch (err) {
|
||
|
|
setError(err instanceof Error ? err.message : 'Unknown error');
|
||
|
|
} finally {
|
||
|
|
setLoading(false);
|
||
|
|
}
|
||
|
|
};
|
||
|
|
|
||
|
|
return (
|
||
|
|
<div className="container mx-auto p-6">
|
||
|
|
<div className="flex justify-between items-center mb-6">
|
||
|
|
<h1 className="text-3xl font-bold">Addigy Devices</h1>
|
||
|
|
<div className="flex items-center gap-4">
|
||
|
|
<label className="flex items-center gap-2">
|
||
|
|
<input
|
||
|
|
type="checkbox"
|
||
|
|
checked={filterOnline}
|
||
|
|
onChange={(e) => setFilterOnline(e.target.checked)}
|
||
|
|
className="w-4 h-4"
|
||
|
|
/>
|
||
|
|
<span>Online Only</span>
|
||
|
|
</label>
|
||
|
|
<button
|
||
|
|
onClick={fetchDevices}
|
||
|
|
disabled={loading}
|
||
|
|
className="px-4 py-2 bg-blue-600 text-white rounded hover:bg-blue-700 disabled:opacity-50"
|
||
|
|
>
|
||
|
|
{loading ? 'Loading...' : 'Refresh'}
|
||
|
|
</button>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
{error && (
|
||
|
|
<div className="bg-red-50 border border-red-200 text-red-800 px-4 py-3 rounded mb-4">
|
||
|
|
<strong>Error:</strong> {error}
|
||
|
|
</div>
|
||
|
|
)}
|
||
|
|
|
||
|
|
{loading ? (
|
||
|
|
<div className="text-center py-12">
|
||
|
|
<div className="inline-block animate-spin rounded-full h-12 w-12 border-b-2 border-blue-600"></div>
|
||
|
|
<p className="mt-4 text-gray-600">Loading devices...</p>
|
||
|
|
</div>
|
||
|
|
) : (
|
||
|
|
<>
|
||
|
|
<div className="mb-4 text-gray-600">
|
||
|
|
Found {devices.length} device{devices.length !== 1 ? 's' : ''}
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<div className="bg-white shadow-md rounded-lg overflow-hidden">
|
||
|
|
<div className="overflow-x-auto">
|
||
|
|
<table className="min-w-full divide-y divide-gray-200">
|
||
|
|
<thead className="bg-gray-50">
|
||
|
|
<tr>
|
||
|
|
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
||
|
|
Device Name
|
||
|
|
</th>
|
||
|
|
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
||
|
|
Model
|
||
|
|
</th>
|
||
|
|
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
||
|
|
OS Version
|
||
|
|
</th>
|
||
|
|
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
||
|
|
Current User
|
||
|
|
</th>
|
||
|
|
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
||
|
|
Status
|
||
|
|
</th>
|
||
|
|
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
||
|
|
Free Disk
|
||
|
|
</th>
|
||
|
|
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
||
|
|
Security
|
||
|
|
</th>
|
||
|
|
</tr>
|
||
|
|
</thead>
|
||
|
|
<tbody className="bg-white divide-y divide-gray-200">
|
||
|
|
{devices.map((device) => (
|
||
|
|
<tr key={device.agentid} className="hover:bg-gray-50">
|
||
|
|
<td className="px-6 py-4 whitespace-nowrap">
|
||
|
|
<div className="text-sm font-medium text-gray-900">
|
||
|
|
{device['Device Name']}
|
||
|
|
</div>
|
||
|
|
<div className="text-xs text-gray-500">
|
||
|
|
{device['Serial Number'] || 'N/A'}
|
||
|
|
</div>
|
||
|
|
</td>
|
||
|
|
<td className="px-6 py-4 whitespace-nowrap text-sm text-gray-900">
|
||
|
|
{device['Device Model Name'] || 'Unknown'}
|
||
|
|
</td>
|
||
|
|
<td className="px-6 py-4 whitespace-nowrap text-sm text-gray-900">
|
||
|
|
{device['MAC OS X Version'] ||
|
||
|
|
device['iOS Version'] ||
|
||
|
|
'N/A'}
|
||
|
|
</td>
|
||
|
|
<td className="px-6 py-4 whitespace-nowrap text-sm text-gray-900">
|
||
|
|
{device['Current User'] || 'N/A'}
|
||
|
|
</td>
|
||
|
|
<td className="px-6 py-4 whitespace-nowrap">
|
||
|
|
<span
|
||
|
|
className={`px-2 py-1 inline-flex text-xs leading-5 font-semibold rounded-full ${
|
||
|
|
device.online
|
||
|
|
? 'bg-green-100 text-green-800'
|
||
|
|
: 'bg-gray-100 text-gray-800'
|
||
|
|
}`}
|
||
|
|
>
|
||
|
|
{device.online ? 'Online' : 'Offline'}
|
||
|
|
</span>
|
||
|
|
</td>
|
||
|
|
<td className="px-6 py-4 whitespace-nowrap text-sm">
|
||
|
|
{device['Free Disk Percentage'] !== undefined ? (
|
||
|
|
<div className="flex items-center">
|
||
|
|
<span
|
||
|
|
className={`${
|
||
|
|
device['Free Disk Percentage'] < 20
|
||
|
|
? 'text-red-600'
|
||
|
|
: device['Free Disk Percentage'] < 40
|
||
|
|
? 'text-yellow-600'
|
||
|
|
: 'text-green-600'
|
||
|
|
}`}
|
||
|
|
>
|
||
|
|
{device['Free Disk Percentage']}%
|
||
|
|
</span>
|
||
|
|
</div>
|
||
|
|
) : (
|
||
|
|
'N/A'
|
||
|
|
)}
|
||
|
|
</td>
|
||
|
|
<td className="px-6 py-4 whitespace-nowrap text-sm">
|
||
|
|
<div className="flex flex-col gap-1">
|
||
|
|
<span
|
||
|
|
className={`text-xs ${
|
||
|
|
device['Firewall Enabled']
|
||
|
|
? 'text-green-600'
|
||
|
|
: 'text-red-600'
|
||
|
|
}`}
|
||
|
|
>
|
||
|
|
FW: {device['Firewall Enabled'] ? '✓' : '✗'}
|
||
|
|
</span>
|
||
|
|
<span
|
||
|
|
className={`text-xs ${
|
||
|
|
device['FileVault Enabled']
|
||
|
|
? 'text-green-600'
|
||
|
|
: 'text-red-600'
|
||
|
|
}`}
|
||
|
|
>
|
||
|
|
FV: {device['FileVault Enabled'] ? '✓' : '✗'}
|
||
|
|
</span>
|
||
|
|
</div>
|
||
|
|
</td>
|
||
|
|
</tr>
|
||
|
|
))}
|
||
|
|
</tbody>
|
||
|
|
</table>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
</>
|
||
|
|
)}
|
||
|
|
</div>
|
||
|
|
);
|
||
|
|
}
|