'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

); }