'use client';
import { useState, useEffect } from 'react';
import Link from 'next/link';
import { Button } from '@/components/ui/button';
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs';
import {
Table,
TableBody,
TableCell,
TableHead,
TableHeader,
TableRow,
} from '@/components/ui/table';
import { StatusBadge } from '@/components/ui/status-badge';
import {
ArrowLeft, Activity, History, Monitor, Loader2, RefreshCw,
ExternalLink, Server, Wifi, WifiOff, AlertTriangle, Bell, XCircle, CheckCircle2, Clock,
} from 'lucide-react';
function fmtDate(d: string | null) {
if (!d) return 'Never';
return new Date(d).toLocaleString(undefined, { month: 'short', day: 'numeric', year: 'numeric', hour: '2-digit', minute: '2-digit' });
}
function StatCard({ label, value, sub, icon: Icon, cls }: { label: string; value: string | number; sub?: string; icon?: React.ElementType; cls?: string }) {
return (
{Icon && }{label}
{value}
{sub &&
{sub}
}
);
}
function StatusTab({ data, onSync, syncing }: { data: any; onSync: () => void; syncing: boolean }) {
if (!data) return
;
const devs = data.devices ?? {};
const alerts = data.openAlerts ?? {};
return (
{data.configured ? 'Connected' : 'Not configured'}
Last sync: {fmtDate(data.lastSync)}
0 ? 'border-yellow-500/30 bg-yellow-500/5' : ''} />
0 ? 'border-red-500/30 bg-red-500/5' : ''} />
0 ? 'border-red-500/30 bg-red-500/5' : ''} />
{(alerts.critical > 0 || alerts.high > 0) && (
Attention Required
{alerts.critical > 0 && (
{alerts.critical} critical alert{alerts.critical !== 1 ? 's' : ''}
)}
{alerts.high > 0 && (
{alerts.high} high priority alert{alerts.high !== 1 ? 's' : ''}
)}
)}
);
}
function HistoryTab({ refreshKey }: { refreshKey: number }) {
const [rows, setRows] = useState([]);
const [loading, setLoading] = useState(true);
useEffect(() => {
setLoading(true);
fetch('/api/sync/history?entityType=datto_rmm&limit=50')
.then(r => r.json())
.then(d => setRows(d.history ?? []))
.catch(() => setRows([]))
.finally(() => setLoading(false));
}, [refreshKey]);
if (loading) return
;
if (!rows.length) return No sync history yet — run a sync to populate
;
return (
Type
Status
Records
Started
Duration
{rows.map((row: any, i: number) => {
const dur = row.completed_at && row.started_at
? Math.round((new Date(row.completed_at).getTime() - new Date(row.started_at).getTime()) / 1000)
: null;
const tone = row.status === 'completed' ? 'ok' : row.status === 'failed' ? 'error' : 'warn';
return (
{row.sync_type}
{row.status}
{row.records_added ?? 0}
{fmtDate(row.started_at)}
{dur != null ? `${dur}s` : '—'}
);
})}
);
}
export default function DattoRmmPage() {
const [status, setStatus] = useState(null);
const [syncing, setSyncing] = useState(false);
const [refreshKey, setRefreshKey] = useState(0);
const fetchStatus = async () => {
const res = await fetch('/api/integrations/status');
if (res.ok) { const d = await res.json(); setStatus(d.dattoRmm); }
};
useEffect(() => { fetchStatus(); }, [refreshKey]);
const handleSync = async () => {
setSyncing(true);
try {
await fetch('/api/datto-rmm/sync', {
method: 'POST',
body: JSON.stringify({ syncType: 'full' }),
headers: { 'Content-Type': 'application/json' },
});
const poll = setInterval(async () => {
const r = await fetch('/api/datto-rmm/sync');
if (r.ok) {
const d = await r.json();
if (!d.isSyncing) {
clearInterval(poll);
setSyncing(false);
setRefreshKey(k => k + 1);
}
}
}, 5000);
} catch {
setSyncing(false);
}
};
return (
RMM — Datto RMM
Sites, devices, alerts, patch management
Status
History
About
Synced Entities
- Sites — RMM sites with device counts, mapped to Autotask companies
- Devices — all managed devices with OS, IP, AV, patch status, UDFs
- Open Alerts — active alerts with priority, device context, ticket linkage
- Resolved Alerts — recent resolved alerts with response action history
Authentication
OAuth2 password grant — API key + secret → Bearer token (100h TTL, refreshed at 50min)
Rate limit: 600 requests / 60 seconds across the account
);
}