- Add admin dashboard with sync controls and data browser - Implement RMM, Auvik, and Addigy organization mappings - Add chunked ticket sync with progress tracking - Implement entity sync service with rate limiting - Add analytics engine and performance optimizer - Create data browser for all PSA entities - Add navigation components and UI improvements - Implement background processing and sync services - Add comprehensive documentation and migration scripts - Update configuration items with multi-system support - Enhance contact management and purchase history - Add issue type assignment and LLM analyzer - Improve error handling and logging utilities
245 lines
8.8 KiB
TypeScript
245 lines
8.8 KiB
TypeScript
import { AuvikDevice } from '@/lib/types/auvik';
|
|
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
|
|
import { Badge } from '@/components/ui/badge';
|
|
import { Label } from '@/components/ui/label';
|
|
import { Network, Info, Wifi, XCircle, AlertCircle } from 'lucide-react';
|
|
|
|
interface AuvikTabProps {
|
|
device?: AuvikDevice;
|
|
}
|
|
|
|
export function AuvikTab({ device }: AuvikTabProps) {
|
|
if (!device) {
|
|
return (
|
|
<div className="flex flex-col items-center justify-center py-12 text-center">
|
|
<Network className="w-16 h-16 text-muted-foreground mb-4" />
|
|
<h3 className="text-lg font-semibold mb-2">No Auvik data available</h3>
|
|
<p className="text-sm text-muted-foreground max-w-md">
|
|
This device is not monitored by Auvik or could not be matched to an Auvik device.
|
|
</p>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
const formatDate = (dateString?: string) => {
|
|
if (!dateString) return 'N/A';
|
|
return new Date(dateString).toLocaleString();
|
|
};
|
|
|
|
const getStatusBadge = () => {
|
|
switch (device.onlineStatus) {
|
|
case 'online':
|
|
return (
|
|
<Badge className="bg-green-100 text-green-800 dark:bg-green-900 dark:text-green-100">
|
|
<Wifi className="w-3 h-3 mr-1" />
|
|
Online
|
|
</Badge>
|
|
);
|
|
case 'offline':
|
|
return (
|
|
<Badge variant="secondary">
|
|
<XCircle className="w-3 h-3 mr-1" />
|
|
Offline
|
|
</Badge>
|
|
);
|
|
default:
|
|
return (
|
|
<Badge variant="outline">
|
|
<AlertCircle className="w-3 h-3 mr-1" />
|
|
Unknown
|
|
</Badge>
|
|
);
|
|
}
|
|
};
|
|
|
|
return (
|
|
<div className="space-y-6">
|
|
{/* Status Header */}
|
|
<div className="flex items-center justify-between">
|
|
<div className="flex items-center gap-2">
|
|
<Network className="w-5 h-5 text-muted-foreground" />
|
|
<h3 className="text-lg font-semibold">{device.deviceName}</h3>
|
|
</div>
|
|
{getStatusBadge()}
|
|
</div>
|
|
|
|
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
|
|
{/* Basic Information */}
|
|
<Card>
|
|
<CardHeader>
|
|
<CardTitle className="text-sm font-medium flex items-center gap-2">
|
|
<Info className="w-4 h-4" />
|
|
Basic Information
|
|
</CardTitle>
|
|
</CardHeader>
|
|
<CardContent className="space-y-3">
|
|
<div>
|
|
<Label className="text-xs text-muted-foreground">Device Name</Label>
|
|
<p className="text-sm font-medium">{device.deviceName || 'N/A'}</p>
|
|
</div>
|
|
<div>
|
|
<Label className="text-xs text-muted-foreground">Device Type</Label>
|
|
<p className="text-sm font-medium capitalize">{device.deviceType || 'N/A'}</p>
|
|
</div>
|
|
<div>
|
|
<Label className="text-xs text-muted-foreground">Serial Number</Label>
|
|
<p className="text-sm font-medium font-mono">{device.serialNumber || 'N/A'}</p>
|
|
</div>
|
|
<div>
|
|
<Label className="text-xs text-muted-foreground">Manufacturer</Label>
|
|
<p className="text-sm font-medium">{device.manufacturer || device.vendorName || 'N/A'}</p>
|
|
</div>
|
|
<div>
|
|
<Label className="text-xs text-muted-foreground">Model</Label>
|
|
<p className="text-sm font-medium">{device.model || device.makeModel || 'N/A'}</p>
|
|
</div>
|
|
</CardContent>
|
|
</Card>
|
|
|
|
{/* Network Information */}
|
|
<Card>
|
|
<CardHeader>
|
|
<CardTitle className="text-sm font-medium flex items-center gap-2">
|
|
<Network className="w-4 h-4" />
|
|
Network Information
|
|
</CardTitle>
|
|
</CardHeader>
|
|
<CardContent className="space-y-3">
|
|
<div>
|
|
<Label className="text-xs text-muted-foreground">IP Addresses</Label>
|
|
{device.ipAddresses && device.ipAddresses.length > 0 ? (
|
|
<div className="flex flex-wrap gap-1 mt-1">
|
|
{device.ipAddresses.map((ip, index) => (
|
|
<Badge key={index} variant="outline" className="font-mono text-xs">
|
|
{ip}
|
|
</Badge>
|
|
))}
|
|
</div>
|
|
) : (
|
|
<p className="text-sm text-muted-foreground">N/A</p>
|
|
)}
|
|
</div>
|
|
<div>
|
|
<Label className="text-xs text-muted-foreground">MAC Addresses</Label>
|
|
{device.macAddresses && device.macAddresses.length > 0 ? (
|
|
<div className="flex flex-wrap gap-1 mt-1">
|
|
{device.macAddresses.map((mac, index) => (
|
|
<Badge key={index} variant="outline" className="font-mono text-xs">
|
|
{mac}
|
|
</Badge>
|
|
))}
|
|
</div>
|
|
) : (
|
|
<p className="text-sm text-muted-foreground">N/A</p>
|
|
)}
|
|
</div>
|
|
<div>
|
|
<Label className="text-xs text-muted-foreground">Tenant</Label>
|
|
<p className="text-sm font-medium">{device.tenantName || 'N/A'}</p>
|
|
</div>
|
|
</CardContent>
|
|
</Card>
|
|
|
|
{/* Status Information */}
|
|
<Card>
|
|
<CardHeader>
|
|
<CardTitle className="text-sm font-medium flex items-center gap-2">
|
|
<Wifi className="w-4 h-4" />
|
|
Status Information
|
|
</CardTitle>
|
|
</CardHeader>
|
|
<CardContent className="space-y-3">
|
|
<div>
|
|
<Label className="text-xs text-muted-foreground">Online Status</Label>
|
|
<div className="mt-1">{getStatusBadge()}</div>
|
|
</div>
|
|
<div>
|
|
<Label className="text-xs text-muted-foreground">Last Seen</Label>
|
|
<p className="text-sm font-medium">{formatDate(device.lastSeenTime)}</p>
|
|
</div>
|
|
</CardContent>
|
|
</Card>
|
|
|
|
{/* Firmware Information */}
|
|
<Card>
|
|
<CardHeader>
|
|
<CardTitle className="text-sm font-medium flex items-center gap-2">
|
|
<Info className="w-4 h-4" />
|
|
Firmware Information
|
|
</CardTitle>
|
|
</CardHeader>
|
|
<CardContent className="space-y-3">
|
|
<div>
|
|
<Label className="text-xs text-muted-foreground">Firmware Version</Label>
|
|
<p className="text-sm font-medium font-mono">
|
|
{device.firmwareVersion || 'N/A'}
|
|
</p>
|
|
</div>
|
|
<div>
|
|
<Label className="text-xs text-muted-foreground">Software Version</Label>
|
|
<p className="text-sm font-medium font-mono">
|
|
{device.softwareVersion || 'N/A'}
|
|
</p>
|
|
</div>
|
|
</CardContent>
|
|
</Card>
|
|
</div>
|
|
|
|
{/* Description */}
|
|
{device.description && (
|
|
<Card>
|
|
<CardHeader>
|
|
<CardTitle className="text-sm font-medium">Description</CardTitle>
|
|
</CardHeader>
|
|
<CardContent>
|
|
<p className="text-sm text-muted-foreground">{device.description}</p>
|
|
</CardContent>
|
|
</Card>
|
|
)}
|
|
|
|
{/* Network Interfaces */}
|
|
{device.networkInterfaces && device.networkInterfaces.length > 0 && (
|
|
<Card>
|
|
<CardHeader>
|
|
<CardTitle className="text-sm font-medium flex items-center gap-2">
|
|
<Network className="w-4 h-4" />
|
|
Network Interfaces
|
|
</CardTitle>
|
|
</CardHeader>
|
|
<CardContent>
|
|
<div className="space-y-2">
|
|
{device.networkInterfaces.map((iface, index) => (
|
|
<div
|
|
key={index}
|
|
className="flex items-center justify-between p-2 border rounded-md"
|
|
>
|
|
<div className="flex-1">
|
|
<p className="text-sm font-medium">{iface.interfaceName}</p>
|
|
{iface.macAddress && (
|
|
<p className="text-xs text-muted-foreground font-mono">
|
|
{iface.macAddress}
|
|
</p>
|
|
)}
|
|
</div>
|
|
<div className="flex items-center gap-2">
|
|
{iface.ipAddress && (
|
|
<Badge variant="outline" className="font-mono text-xs">
|
|
{iface.ipAddress}
|
|
</Badge>
|
|
)}
|
|
<Badge
|
|
variant={iface.status === 'up' ? 'default' : 'secondary'}
|
|
className="text-xs"
|
|
>
|
|
{iface.status}
|
|
</Badge>
|
|
</div>
|
|
</div>
|
|
))}
|
|
</div>
|
|
</CardContent>
|
|
</Card>
|
|
)}
|
|
</div>
|
|
);
|
|
}
|