'use client'; import { useState, useEffect, Suspense } from 'react'; import { useSearchParams } from 'next/navigation'; import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'; import { Button } from '@/components/ui/button'; import { Badge } from '@/components/ui/badge'; import { Input } from '@/components/ui/input'; import { Label } from '@/components/ui/label'; import { Skeleton } from '@/components/ui/skeleton'; import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow, } from '@/components/ui/table'; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from '@/components/ui/select'; import { CompanySelectorEnhanced } from '@/components/companies/company-selector-enhanced'; import { ThemeToggle } from '@/components/theme-toggle'; import { ConfigItemModal } from '@/components/configuration-items/config-item-modal'; import { ContactCell } from '@/components/configuration-items/contact-cell'; import { Collapsible, CollapsibleContent, CollapsibleTrigger } from '@/components/ui/collapsible'; import { Checkbox } from '@/components/ui/checkbox'; import { Calendar as CalendarComponent } from '@/components/ui/calendar'; import { Popover, PopoverContent, PopoverTrigger } from '@/components/ui/popover'; import { Server, Monitor, HardDrive, Network, AlertCircle, CheckCircle, XCircle, RefreshCw, Search, Settings, Activity, Cpu, MemoryStick, Wifi, Shield, Calendar as CalendarIcon, Hash, Building2, ArrowLeft, Filter, Download, ChevronRight, Info, Power } from 'lucide-react'; import { format } from 'date-fns'; import { ConfigurationItem } from '@/lib/types/autotask'; import { DattoRMMDevice } from '@/lib/types/datto-rmm'; import { useApi } from '@/lib/hooks/use-api'; interface DeviceComparison { autotaskDevice?: ConfigurationItem; rmmDevice?: DattoRMMDevice; status: 'matched' | 'autotask-only' | 'rmm-only'; matchedBy?: string; } function ConfigurationItemsContent() { const searchParams = useSearchParams(); const [selectedCompany, setSelectedCompany] = useState(); const [selectedCompanyName, setSelectedCompanyName] = useState(''); const [searchTerm, setSearchTerm] = useState(''); const [filterType, setFilterType] = useState('all'); const [configItems, setConfigItems] = useState([]); const [comparison, setComparison] = useState([]); const [stats, setStats] = useState(null); const [loading, setLoading] = useState(false); const [error, setError] = useState(null); const [viewMode, setViewMode] = useState<'autotask' | 'comparison'>('comparison'); const [selectedItemId, setSelectedItemId] = useState(null); const [modalOpen, setModalOpen] = useState(false); const [adminExpanded, setAdminExpanded] = useState(false); const [selectedItems, setSelectedItems] = useState>(new Set()); const [bulkProcessing, setBulkProcessing] = useState(false); const [lastSeenAfterDate, setLastSeenAfterDate] = useState(); const [activeFilter, setActiveFilter] = useState<'active' | 'inactive' | 'all'>('active'); const [displayLimit, setDisplayLimit] = useState(50); // Start with 50 items // Initialize company from URL params on mount useEffect(() => { const companyIdParam = searchParams.get('companyId'); const companyNameParam = searchParams.get('companyName'); if (companyIdParam) { setSelectedCompany(parseInt(companyIdParam)); setSelectedCompanyName(companyNameParam || ''); } }, [searchParams]); // Fetch configuration items when company changes useEffect(() => { if (!selectedCompany) { setConfigItems([]); return; } const fetchConfigItems = async () => { setLoading(true); setError(null); try { // Fetch comparison data (includes both Autotask and RMM) const response = await fetch( `/api/rmm-devices?companyId=${selectedCompany}&companyName=${encodeURIComponent(selectedCompanyName)}&activeFilter=${activeFilter}` ); if (!response.ok) { throw new Error('Failed to fetch devices'); } const data = await response.json(); setComparison(data.comparison || []); setStats(data.stats); } catch (err) { setError(err instanceof Error ? err.message : 'An error occurred'); setComparison([]); } finally { setLoading(false); } }; fetchConfigItems(); }, [selectedCompany, selectedCompanyName, activeFilter]); // Filter comparison items based on search and type const filteredComparison = comparison.filter((item: DeviceComparison) => { const deviceName = item.autotaskDevice?.referenceTitle || item.rmmDevice?.hostname || ''; const serialNumber = item.autotaskDevice?.serialNumber || item.rmmDevice?.serialNumber || ''; const searchLower = searchTerm.toLowerCase(); const matchesSearch = deviceName.toLowerCase().includes(searchLower) || serialNumber.toLowerCase().includes(searchLower); const matchesType = filterType === 'all' || (filterType === 'matched' && item.status === 'matched') || (filterType === 'autotask-only' && item.status === 'autotask-only') || (filterType === 'rmm-only' && item.status === 'rmm-only'); // Filter by last seen date in RMM (after specified date) let matchesLastSeen = true; if (lastSeenAfterDate && item.rmmDevice?.lastSeen) { const lastSeenDate = new Date(item.rmmDevice.lastSeen); matchesLastSeen = lastSeenDate >= lastSeenAfterDate; } return matchesSearch && matchesType && matchesLastSeen; }); // Handle company selection const handleCompanyChange = (companyId: number | undefined, companyName?: string) => { setSelectedCompany(companyId); setSelectedCompanyName(companyName || ''); setSelectedItems(new Set()); // Clear selections when company changes }; const handleSelectItem = (itemId: number, checked: boolean) => { const newSelected = new Set(selectedItems); if (checked) { newSelected.add(itemId); } else { newSelected.delete(itemId); } setSelectedItems(newSelected); }; const handleSelectAll = (checked: boolean) => { if (checked) { const allIds = new Set( filteredComparison .filter(item => item.autotaskDevice?.id) .map(item => item.autotaskDevice!.id) ); setSelectedItems(allIds); } else { setSelectedItems(new Set()); } }; const handleBulkMakeInactive = async () => { if (selectedItems.size === 0) return; setBulkProcessing(true); try { const promises = Array.from(selectedItems).map(itemId => fetch(`/api/configuration-items/${itemId}`, { method: 'PATCH', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ isActive: false }), }) ); await Promise.all(promises); // Refresh the data if (selectedCompany) { const response = await fetch( `/api/rmm-devices?companyId=${selectedCompany}&companyName=${encodeURIComponent(selectedCompanyName)}` ); if (response.ok) { const data = await response.json(); setComparison(data.comparison || []); setStats(data.stats); } } setSelectedItems(new Set()); setAdminExpanded(false); } catch (err) { console.error('Bulk operation failed:', err); setError('Failed to make items inactive'); } finally { setBulkProcessing(false); } }; const getDeviceIcon = (item: ConfigurationItem) => { if (item.rmmDeviceAuditDeviceTypeID) { // You can map device type IDs to specific icons return ; } if (item.dattoSerialNumber) { return ; } return ; }; const getRMMStatus = (item: ConfigurationItem) => { if (item.rmmDeviceUID) { return ( RMM Connected ); } return ( No RMM ); }; const getDattoStatus = (item: ConfigurationItem) => { if (item.dattoSerialNumber) { return ( Datto Protected ); } return null; }; return (
{/* Header */}

Configuration Items

Autotask & RMM Device Management

{/* Main Content */}
{/* Company Selector Card */}
Select Company Choose a company to view their configuration items
{selectedCompany && (

Total Devices

PSA: {stats?.totalAutotask || 0} | RMM: {stats?.totalRmm || 0}

)}
{/* Admin Section */} {selectedCompany && filteredComparison.length > 0 && ( Admin Actions {selectedItems.size > 0 && ( {selectedItems.size} selected )}

Bulk Actions

{selectedItems.size} device{selectedItems.size !== 1 ? 's' : ''} selected

)} {/* Filters and Search */} {selectedCompany && ( Filters & Search
setSearchTerm(e.target.value)} className="pl-8" />
{lastSeenAfterDate && ( )}

Matched

{stats?.matched || 0}

RMM Only

{stats?.rmmOnly || 0}

)} {/* Configuration Items Table */} {selectedCompany && (
Device Comparison {filteredComparison.length} {loading && (
Loading...
)}
{loading ? (
{[1, 2, 3].map(i => ( ))}
) : error ? (
Error: {error}
) : filteredComparison.length === 0 ? (
{!selectedCompany ? (

Select a company to view configuration items

) : searchTerm || filterType !== 'all' ? (

No devices found matching your filters

) : (

No configuration items found for this company

)}
) : (
i.autotaskDevice).length && selectedItems.size > 0} onCheckedChange={handleSelectAll} /> Status Device Name Serial Number IP Address Contact PSA RMM Match Type {filteredComparison.slice(0, displayLimit).map((item: DeviceComparison, index: number) => ( {item.autotaskDevice?.id && ( handleSelectItem(item.autotaskDevice!.id, checked as boolean)} /> )} {item.status === 'matched' && ( Matched )} {item.status === 'autotask-only' && ( AT Only )} {item.status === 'rmm-only' && ( RMM Only )}
{item.autotaskDevice?.referenceTitle || item.rmmDevice?.hostname || 'Unknown Device'}
{(item.autotaskDevice?.rmmDeviceAuditHostname || item.rmmDevice?.description) && (
{item.autotaskDevice?.rmmDeviceAuditHostname || item.rmmDevice?.description}
)}
{item.autotaskDevice?.serialNumber || item.rmmDevice?.serialNumber || '-'} {item.autotaskDevice?.rmmDeviceAuditIPAddress || item.rmmDevice?.intIpAddress || '-'} {item.autotaskDevice ? ( ) : ( )} {item.rmmDevice ? ( ) : ( )} {item.matchedBy && ( {item.matchedBy} )}
))}
{/* Load More Button */} {filteredComparison.length > displayLimit && (
)}
)}
)} {/* Info Card when no company selected */} {!selectedCompany && (

Get Started

Select a company from the dropdown above to view and manage their configuration items. You can compare devices between Autotask and RMM systems.

View Autotask devices
Check RMM status
Monitor Datto devices
)}
{/* Configuration Item Detail Modal */}
); } export default function ConfigurationItemsPage() { return ( Loading...}> ); }