wulf-pulse/components/configuration-items/rmm-tab.tsx

338 lines
12 KiB
TypeScript
Raw Normal View History

'use client';
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
import { Badge } from '@/components/ui/badge';
import { Label } from '@/components/ui/label';
import {
Monitor,
Info,
Network,
HardDrive,
Shield,
Wifi,
XCircle
} from 'lucide-react';
import { format } from 'date-fns';
import { DattoRMMDevice } from '@/lib/types/datto-rmm';
import { lookupDeviceBySerial, formatDeviceInfo } from '@/lib/utils/device-lookup';
interface RMMTabProps {
device?: DattoRMMDevice;
}
export function RMMTab({ device }: RMMTabProps) {
if (!device) {
return (
<Card className="border-0 shadow-lg">
<CardContent className="pt-6">
<div className="text-center py-12 text-muted-foreground">
<Monitor className="w-12 h-12 mx-auto mb-4 opacity-50" />
<p>No RMM data available for this device</p>
</div>
</CardContent>
</Card>
);
}
return (
<Card className="border-0 shadow-lg">
<CardHeader className="bg-gradient-to-r from-gray-50 to-gray-100 dark:from-gray-900 dark:to-gray-800 rounded-t-lg">
<CardTitle className="text-lg">RMM Device Information</CardTitle>
</CardHeader>
<CardContent className="pt-6">
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
{/* Basic Information */}
<div className="space-y-4">
<h3 className="font-semibold flex items-center gap-2">
<Info className="w-4 h-4" />
Basic Information
</h3>
<div className="space-y-3">
<div>
<Label>Hostname</Label>
<p className="text-sm text-muted-foreground mt-1">{device.hostname}</p>
</div>
<div>
<Label>Description</Label>
<p className="text-sm text-muted-foreground mt-1">
{device.description || '-'}
</p>
</div>
<div>
<Label>Serial Number</Label>
<p className="text-sm text-muted-foreground font-mono mt-1">
{device.serialNumber || '-'}
</p>
</div>
<div>
<Label>Device Type</Label>
<p className="text-sm text-muted-foreground mt-1">
{device.deviceType?.type || '-'}
</p>
</div>
<div>
<Label>Status</Label>
<div className="flex items-center gap-2 mt-1">
{device.online ? (
<Badge variant="default" className="bg-green-600">
<Wifi className="w-3 h-3 mr-1" />
Online
</Badge>
) : (
<Badge variant="secondary">
<XCircle className="w-3 h-3 mr-1" />
Offline
</Badge>
)}
{device.rebootRequired && (
<Badge variant="outline" className="text-orange-600">
Reboot Required
</Badge>
)}
</div>
</div>
</div>
</div>
{/* Network Information */}
<div className="space-y-4">
<h3 className="font-semibold flex items-center gap-2">
<Network className="w-4 h-4" />
Network Information
</h3>
<div className="space-y-3">
<div>
<Label>Internal IP</Label>
<p className="text-sm text-muted-foreground font-mono mt-1">
{device.intIpAddress || '-'}
</p>
</div>
<div>
<Label>External IP</Label>
<p className="text-sm text-muted-foreground font-mono mt-1">
{device.extIpAddress || '-'}
</p>
</div>
<div>
<Label>MAC Addresses</Label>
<div className="text-sm text-muted-foreground font-mono mt-1">
{device.macAddresses && device.macAddresses.length > 0 ? (
device.macAddresses.map((mac, i) => (
<div key={i}>{mac}</div>
))
) : (
'-'
)}
</div>
</div>
<div>
<Label>Domain</Label>
<p className="text-sm text-muted-foreground mt-1">
{device.domain || '-'}
</p>
</div>
<div>
<Label>Last Seen</Label>
<p className="text-sm text-muted-foreground mt-1">
{device.lastSeen ?
format(new Date(device.lastSeen), 'MMM d, yyyy h:mm a') :
'-'}
</p>
</div>
<div>
<Label>Last User</Label>
<p className="text-sm text-muted-foreground mt-1">
{device.lastLoggedInUser || '-'}
</p>
</div>
</div>
</div>
{/* System Information */}
<div className="space-y-4">
<h3 className="font-semibold flex items-center gap-2">
<HardDrive className="w-4 h-4" />
System Information
</h3>
<div className="space-y-3">
<div>
<Label>Operating System</Label>
<p className="text-sm text-muted-foreground mt-1">
{device.operatingSystem || '-'}
</p>
{device.a64Bit !== undefined && (
<Badge variant="outline" className="text-xs mt-1">
{device.a64Bit ? '64-bit' : '32-bit'}
</Badge>
)}
</div>
<div>
<Label>Device Category</Label>
<p className="text-sm text-muted-foreground mt-1">
{device.deviceType?.category || '-'}
</p>
</div>
<div>
<Label>Manufacturer</Label>
<p className="text-sm text-muted-foreground mt-1">
{(() => {
if (device.manufacturer) return device.manufacturer;
const deviceInfo = lookupDeviceBySerial(device.serialNumber);
return deviceInfo?.manufacturer || '-';
})()}
</p>
</div>
<div>
<Label>Model</Label>
<p className="text-sm text-muted-foreground mt-1">
{(() => {
if (device.model) return device.model;
const deviceInfo = lookupDeviceBySerial(device.serialNumber);
return deviceInfo?.estimatedModel || deviceInfo?.modelFamily || '-';
})()}
</p>
{!device.model && device.serialNumber && (
<p className="text-xs text-muted-foreground mt-1 italic">
Estimated from serial: {device.serialNumber}
</p>
)}
</div>
<div>
<Label>CPU</Label>
<p className="text-sm text-muted-foreground mt-1">
{device.cpuName || '-'} {device.cpuCores ? `(${device.cpuCores} cores)` : ''}
</p>
</div>
<div>
<Label>Memory</Label>
<p className="text-sm text-muted-foreground mt-1">
{device.memory ? `${(device.memory / 1024).toFixed(2)} GB` : '-'}
</p>
</div>
<div>
<Label>Total Disk Size</Label>
<p className="text-sm text-muted-foreground mt-1">
{device.diskSize ? `${(device.diskSize / (1024 * 1024 * 1024)).toFixed(2)} GB` : '-'}
</p>
</div>
<div>
<Label>Agent Version</Label>
<p className="text-sm text-muted-foreground mt-1">
{device.displayVersion || device.cagVersion || '-'}
</p>
</div>
<div>
<Label>Last Reboot</Label>
<p className="text-sm text-muted-foreground mt-1">
{device.lastReboot ?
format(new Date(device.lastReboot), 'MMM d, yyyy h:mm a') :
'-'}
</p>
</div>
<div>
<Label>Created Date</Label>
<p className="text-sm text-muted-foreground mt-1">
{device.creationDate ?
format(new Date(device.creationDate), 'MMM d, yyyy') :
'-'}
</p>
</div>
{device.warrantyDate && (
<div>
<Label>Warranty Expiration</Label>
<p className="text-sm text-muted-foreground mt-1">
{format(new Date(device.warrantyDate), 'MMM d, yyyy')}
</p>
</div>
)}
</div>
</div>
{/* Security Information */}
<div className="space-y-4">
<h3 className="font-semibold flex items-center gap-2">
<Shield className="w-4 h-4" />
Security Information
</h3>
<div className="space-y-3">
<div>
<Label>Antivirus</Label>
<div className="space-y-1 mt-1">
<p className="text-sm text-muted-foreground">
{device.antivirus?.antivirusProduct || 'Not detected'}
</p>
{device.antivirus?.antivirusStatus && (
<Badge
variant={device.antivirus.antivirusStatus === 'RunningAndUpToDate' ? 'default' : 'secondary'}
className={device.antivirus.antivirusStatus === 'RunningAndUpToDate' ? 'bg-green-600' : ''}
>
{device.antivirus.antivirusStatus.replace(/([A-Z])/g, ' $1').trim()}
</Badge>
)}
</div>
</div>
<div>
<Label>Patch Management</Label>
<div className="space-y-2 mt-1">
{device.patchManagement?.patchStatus && (
<Badge
variant={device.patchManagement.patchStatus === 'FullyPatched' ? 'default' : 'secondary'}
className={device.patchManagement.patchStatus === 'FullyPatched' ? 'bg-green-600' : ''}
>
{device.patchManagement.patchStatus.replace(/([A-Z])/g, ' $1').trim()}
</Badge>
)}
<div className="flex gap-4">
<div>
<p className="text-xs text-muted-foreground">Pending</p>
<p className="text-lg font-semibold text-orange-600">
{device.patchManagement?.patchesApprovedPending || 0}
</p>
</div>
<div>
<p className="text-xs text-muted-foreground">Installed</p>
<p className="text-lg font-semibold text-green-600">
{device.patchManagement?.patchesInstalled || 0}
</p>
</div>
<div>
<p className="text-xs text-muted-foreground">Not Approved</p>
<p className="text-lg font-semibold text-gray-600">
{device.patchManagement?.patchesNotApproved || 0}
</p>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</CardContent>
</Card>
);
}