163 lines
6.9 KiB
TypeScript
163 lines
6.9 KiB
TypeScript
import { auth } from '@/auth'
|
|
import { db } from '@/lib/db'
|
|
import { machines, sessions } from '@/lib/db/schema'
|
|
import { eq, desc } from 'drizzle-orm'
|
|
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'
|
|
import { Button } from '@/components/ui/button'
|
|
import Link from 'next/link'
|
|
import {
|
|
Laptop,
|
|
History,
|
|
Link2,
|
|
ArrowRight,
|
|
Clock,
|
|
CheckCircle2,
|
|
Circle
|
|
} from 'lucide-react'
|
|
import { formatDistanceToNow } from 'date-fns'
|
|
|
|
export default async function DashboardPage() {
|
|
const session = await auth()
|
|
const userId = session!.user.id
|
|
|
|
const [machineList, sessionList] = await Promise.all([
|
|
db.select().from(machines).where(eq(machines.userId, userId)).orderBy(desc(machines.lastSeen)),
|
|
db.select().from(sessions).where(eq(sessions.viewerUserId, userId)).orderBy(desc(sessions.startedAt)).limit(5),
|
|
])
|
|
|
|
const onlineMachines = machineList.filter((m) => m.isOnline)
|
|
|
|
return (
|
|
<div className="space-y-6">
|
|
<div className="grid gap-4 md:grid-cols-3">
|
|
<Card className="border-border/50">
|
|
<CardHeader className="flex flex-row items-center justify-between pb-2">
|
|
<CardTitle className="text-sm font-medium text-muted-foreground">Total Machines</CardTitle>
|
|
<Laptop className="h-4 w-4 text-muted-foreground" />
|
|
</CardHeader>
|
|
<CardContent>
|
|
<div className="text-2xl font-bold">{machineList.length}</div>
|
|
<p className="text-xs text-muted-foreground mt-1">{onlineMachines.length} online</p>
|
|
</CardContent>
|
|
</Card>
|
|
|
|
<Card className="border-border/50">
|
|
<CardHeader className="flex flex-row items-center justify-between pb-2">
|
|
<CardTitle className="text-sm font-medium text-muted-foreground">Recent Sessions</CardTitle>
|
|
<History className="h-4 w-4 text-muted-foreground" />
|
|
</CardHeader>
|
|
<CardContent>
|
|
<div className="text-2xl font-bold">{sessionList.length}</div>
|
|
<p className="text-xs text-muted-foreground mt-1">Last 5 sessions</p>
|
|
</CardContent>
|
|
</Card>
|
|
|
|
<Card className="border-border/50">
|
|
<CardHeader className="flex flex-row items-center justify-between pb-2">
|
|
<CardTitle className="text-sm font-medium text-muted-foreground">Quick Connect</CardTitle>
|
|
<Link2 className="h-4 w-4 text-muted-foreground" />
|
|
</CardHeader>
|
|
<CardContent>
|
|
<Button asChild size="sm" className="w-full">
|
|
<Link href="/dashboard/connect">
|
|
Enter Code <ArrowRight className="ml-2 h-4 w-4" />
|
|
</Link>
|
|
</Button>
|
|
</CardContent>
|
|
</Card>
|
|
</div>
|
|
|
|
<div className="grid gap-6 lg:grid-cols-2">
|
|
<Card className="border-border/50">
|
|
<CardHeader className="flex flex-row items-center justify-between">
|
|
<div>
|
|
<CardTitle>Your Machines</CardTitle>
|
|
<CardDescription>Registered remote machines</CardDescription>
|
|
</div>
|
|
<Button variant="outline" size="sm" asChild>
|
|
<Link href="/dashboard/machines">View all</Link>
|
|
</Button>
|
|
</CardHeader>
|
|
<CardContent>
|
|
{machineList.length > 0 ? (
|
|
<div className="space-y-3">
|
|
{machineList.slice(0, 5).map((machine) => (
|
|
<div key={machine.id} className="flex items-center justify-between p-3 rounded-lg bg-muted/30">
|
|
<div className="flex items-center gap-3">
|
|
<div className={`h-2 w-2 rounded-full ${machine.isOnline ? 'bg-green-500' : 'bg-muted-foreground/30'}`} />
|
|
<div>
|
|
<p className="text-sm font-medium">{machine.name}</p>
|
|
<p className="text-xs text-muted-foreground">{machine.os} {machine.osVersion}</p>
|
|
</div>
|
|
</div>
|
|
<div className="text-xs text-muted-foreground">
|
|
{machine.lastSeen
|
|
? formatDistanceToNow(new Date(machine.lastSeen), { addSuffix: true })
|
|
: 'Never connected'}
|
|
</div>
|
|
</div>
|
|
))}
|
|
</div>
|
|
) : (
|
|
<div className="flex flex-col items-center justify-center py-8 text-center">
|
|
<Laptop className="h-10 w-10 text-muted-foreground/30 mb-4" />
|
|
<p className="text-sm text-muted-foreground mb-4">No machines registered yet</p>
|
|
<Button variant="outline" size="sm" asChild>
|
|
<Link href="/download">Download Agent</Link>
|
|
</Button>
|
|
</div>
|
|
)}
|
|
</CardContent>
|
|
</Card>
|
|
|
|
<Card className="border-border/50">
|
|
<CardHeader className="flex flex-row items-center justify-between">
|
|
<div>
|
|
<CardTitle>Recent Sessions</CardTitle>
|
|
<CardDescription>Your connection history</CardDescription>
|
|
</div>
|
|
<Button variant="outline" size="sm" asChild>
|
|
<Link href="/dashboard/sessions">View all</Link>
|
|
</Button>
|
|
</CardHeader>
|
|
<CardContent>
|
|
{sessionList.length > 0 ? (
|
|
<div className="space-y-3">
|
|
{sessionList.map((s) => (
|
|
<div key={s.id} className="flex items-center justify-between p-3 rounded-lg bg-muted/30">
|
|
<div className="flex items-center gap-3">
|
|
{s.endedAt ? (
|
|
<CheckCircle2 className="h-4 w-4 text-muted-foreground" />
|
|
) : (
|
|
<Circle className="h-4 w-4 text-green-500" />
|
|
)}
|
|
<div>
|
|
<p className="text-sm font-medium">{s.machineName || 'Unknown Machine'}</p>
|
|
<p className="text-xs text-muted-foreground">
|
|
{s.connectionType === 'session_code' ? 'Session Code' : 'Direct'} connection
|
|
</p>
|
|
</div>
|
|
</div>
|
|
<div className="flex items-center gap-2 text-xs text-muted-foreground">
|
|
<Clock className="h-3 w-3" />
|
|
{formatDistanceToNow(new Date(s.startedAt), { addSuffix: true })}
|
|
</div>
|
|
</div>
|
|
))}
|
|
</div>
|
|
) : (
|
|
<div className="flex flex-col items-center justify-center py-8 text-center">
|
|
<History className="h-10 w-10 text-muted-foreground/30 mb-4" />
|
|
<p className="text-sm text-muted-foreground mb-4">No sessions yet</p>
|
|
<Button variant="outline" size="sm" asChild>
|
|
<Link href="/dashboard/connect">Start connecting</Link>
|
|
</Button>
|
|
</div>
|
|
)}
|
|
</CardContent>
|
|
</Card>
|
|
</div>
|
|
</div>
|
|
)
|
|
}
|