Files
move-data-nas/web/src/lib/status.ts
T
darroyo 7a2a7a4935 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
2026-07-10 10:51:10 -04:00

104 lines
3.4 KiB
TypeScript

import { cva, type VariantProps } from 'class-variance-authority'
export const badgeVariants = cva(
'inline-flex items-center gap-1.5 rounded-full px-2.5 py-0.5 text-xs font-medium transition-colors',
{
variants: {
variant: {
running: 'bg-emerald-500/15 text-emerald-400 border border-emerald-500/30',
pending: 'bg-amber-500/15 text-amber-400 border border-amber-500/30',
waking: 'bg-amber-500/15 text-amber-400 border border-amber-500/30',
success: 'bg-emerald-500/15 text-emerald-400 border border-emerald-500/30',
error: 'bg-rose-500/15 text-rose-400 border border-rose-500/30',
info: 'bg-sky-500/15 text-sky-400 border border-sky-500/30',
neutral: 'bg-zinc-500/15 text-zinc-400 border border-zinc-500/30',
},
},
defaultVariants: {
variant: 'neutral',
},
}
)
export type BadgeVariant = VariantProps<typeof badgeVariants>['variant']
const STATUS_TO_VARIANT: Record<string, BadgeVariant> = {
running: 'running',
pending: 'pending',
waking: 'pending',
success: 'success',
error: 'error',
failed: 'error',
completed: 'success',
cancelled: 'neutral',
info: 'info',
unknown: 'neutral',
online: 'success',
offline: 'error',
}
export function statusVariant(status: string): BadgeVariant {
return STATUS_TO_VARIANT[status.toLowerCase()] ?? 'neutral'
}
export function statusLabel(status: string): string {
const labels: Record<string, string> = {
running: 'Running',
pending: 'Pending',
waking: 'Waking',
success: 'Success',
error: 'Error',
failed: 'Failed',
completed: 'Completed',
cancelled: 'Cancelled',
info: 'Info',
unknown: 'Unknown',
}
return labels[status.toLowerCase()] ?? status
}
export interface ErrorCodeInfo {
title: string;
hint: string;
}
const ERROR_CODES: Record<string, ErrorCodeInfo> = {
wol_timeout: {
title: "Machine didn't wake up",
hint: 'Waited for machine to respond and got no SSH reply. Make sure the server and the target machine are on the same L2 broadcast domain. For HP Microservers and similar hardware, increase the Wake timeout in the machine settings (180s or more).',
},
wol_send_failed: {
title: 'Wake-on-LAN packet failed to send',
hint: 'The magic packet could not be sent. Check the server logs for details. The server must be on the same broadcast domain as the target machine NIC and must have NET_ADMIN or CAP_NET_RAW capability.',
},
rsync_error: {
title: 'rsync failed',
hint: 'See the full log for rsync error details',
},
exit_code: {
title: 'rsync exited with errors',
hint: 'Check stderr output for details',
},
ssh_key_fallback: {
title: 'SSH key not found',
hint: 'Server fell back to its own key. Verify the machine SSH key configuration',
},
cancelled_user: {
title: 'Cancelled by user',
hint: 'The job was manually cancelled from the UI',
},
cancelled_shutdown: {
title: 'Cancelled — server shutdown',
hint: 'The job was interrupted because the syncserver process stopped',
},
unknown_cancelled: {
title: 'Cancelled (no reason recorded)',
hint: 'This cancellation happened before the upgrade that records reasons',
},
}
export function getErrorCodeInfo(code: string | null | undefined): ErrorCodeInfo | null {
if (!code) return null
return ERROR_CODES[code] ?? { title: code, hint: 'See full error details below' }
}