Add Last Seen column and color-coded status badges in Machines table

- New Last Seen column showing last_seen_at timestamp per machine
- Status badges: online=green (success), offline=red (error), waking/pending=amber
- Update Machine interface with last_seen_at and created_at fields
This commit is contained in:
2026-07-10 10:51:10 -04:00
parent 175fdf4dff
commit 7a2a7a4935
5 changed files with 15 additions and 10 deletions
+1 -1
View File
@@ -1,5 +1,5 @@
BINARY=syncserver BINARY=syncserver
VERSION?=1.0.40 VERSION?=1.0.41
GO?=go GO?=go
LDFLAGS=-s -w -X main.version=$(VERSION) -X main.commit=$(shell git rev-parse --short HEAD 2>/dev/null || echo unknown) LDFLAGS=-s -w -X main.version=$(VERSION) -X main.commit=$(shell git rev-parse --short HEAD 2>/dev/null || echo unknown)
BUILD_FLAGS=CGO_ENABLED=0 BUILD_FLAGS=CGO_ENABLED=0
+1 -1
View File
@@ -20,7 +20,7 @@ import (
"github.com/syncserver/internal/syncengine" "github.com/syncserver/internal/syncengine"
) )
var version = "1.0.40" var version = "1.0.41"
func main() { func main() {
cfgPath := flag.String("config", "", "Path to config.yaml") cfgPath := flag.String("config", "", "Path to config.yaml")
+2
View File
@@ -53,6 +53,8 @@ export interface Machine {
fingerprint_confirmed: boolean; fingerprint_confirmed: boolean;
host_key_fingerprint: string | null; host_key_fingerprint: string | null;
status: string; status: string;
last_seen_at: string | null;
created_at: string;
} }
export interface TestConnectionResponse { export interface TestConnectionResponse {
+3 -1
View File
@@ -25,7 +25,7 @@ export type BadgeVariant = VariantProps<typeof badgeVariants>['variant']
const STATUS_TO_VARIANT: Record<string, BadgeVariant> = { const STATUS_TO_VARIANT: Record<string, BadgeVariant> = {
running: 'running', running: 'running',
pending: 'pending', pending: 'pending',
waking: 'waking', waking: 'pending',
success: 'success', success: 'success',
error: 'error', error: 'error',
failed: 'error', failed: 'error',
@@ -33,6 +33,8 @@ const STATUS_TO_VARIANT: Record<string, BadgeVariant> = {
cancelled: 'neutral', cancelled: 'neutral',
info: 'info', info: 'info',
unknown: 'neutral', unknown: 'neutral',
online: 'success',
offline: 'error',
} }
export function statusVariant(status: string): BadgeVariant { export function statusVariant(status: string): BadgeVariant {
+8 -7
View File
@@ -5,6 +5,7 @@ import { Input } from '@/components/ui/Input';
import { Label } from '@/components/ui/Label'; import { Label } from '@/components/ui/Label';
import { Select, SelectValue, SelectTrigger, SelectContent, SelectItem } from '@/components/ui/Select'; import { Select, SelectValue, SelectTrigger, SelectContent, SelectItem } from '@/components/ui/Select';
import { Badge } from '@/components/ui/Badge'; import { Badge } from '@/components/ui/Badge';
import { statusVariant } from '@/lib/status';
import { import {
Modal, Modal,
ModalContent, ModalContent,
@@ -278,6 +279,7 @@ export default function Machines() {
<TableHead>WoL</TableHead> <TableHead>WoL</TableHead>
<TableHead>WoL Timeout</TableHead> <TableHead>WoL Timeout</TableHead>
<TableHead>Status</TableHead> <TableHead>Status</TableHead>
<TableHead>Last Seen</TableHead>
<TableHead className="w-24">Actions</TableHead> <TableHead className="w-24">Actions</TableHead>
</TableRow> </TableRow>
</TableHeader> </TableHeader>
@@ -312,6 +314,11 @@ export default function Machines() {
<TableCell> <TableCell>
<StatusBadge status={m.status} /> <StatusBadge status={m.status} />
</TableCell> </TableCell>
<TableCell>
<span className="text-xs text-fg-muted">
{m.last_seen_at ? new Date(m.last_seen_at).toLocaleString() : <span className="text-fg-subtle"></span>}
</span>
</TableCell>
<TableCell> <TableCell>
<div className="flex items-center gap-1"> <div className="flex items-center gap-1">
<Button <Button
@@ -696,11 +703,5 @@ export default function Machines() {
} }
function StatusBadge({ status }: { status: string }) { function StatusBadge({ status }: { status: string }) {
const variant = return <Badge variant={statusVariant(status)} label={status} />;
status === 'online'
? 'success'
: status === 'offline'
? 'neutral'
: 'info';
return <Badge variant={variant} label={status} />;
} }