Simplify host overview and move enrollment into drawer

This commit is contained in:
2026-10-06 17:18:31 -04:00
parent a991198d69
commit ef290ae0ee
11 changed files with 310 additions and 61 deletions
+40
View File
@@ -0,0 +1,40 @@
<script lang="ts">
import type { Host } from '$lib/hosts.js';
import { contextHref } from '$lib/navigation-context.js';
import { hostListStatus, hostReadingsStale, hostCpuUsage, hostUsedSpace } from '$ui/host-overview.js';
import Badge from './primitives/Badge.svelte';
let { hosts, now, returnTo = '/hosts' }: { hosts: (Host & { serverCount: number })[]; now: number; returnTo?: string } = $props();
</script>
<div class="table-wrap"><table>
<thead><tr><th>Host</th><th>Status</th><th title="Assigned servers, including stopped instances">Servers</th><th>CPU</th><th>Memory <span>used / total</span></th><th>Disk <span>used / total</span></th></tr></thead>
<tbody>{#each hosts as host (host.id)}
{@const status = hostListStatus(host, now)}
{@const stale = hostReadingsStale(host, now)}
<tr>
<td><a class="name" href={contextHref(`/hosts/${host.id}`, returnTo)}>{host.name}</a>{#if host.hostname && host.hostname !== host.name}<span class="secondary">{host.hostname}</span>{/if}
{#if stale && host.lastHeartbeat}<span class="secondary">Resource readings stale</span>{/if}</td>
<td class="status"><Badge variant={status.tone}>{status.label}</Badge>{#if status.detail}<a class="reason" href={contextHref(`/hosts/${host.id}?tab=${status.tab}`, returnTo)}>{status.detail}</a>{/if}</td>
<td><a href={contextHref(`/hosts/${host.id}?tab=servers`, returnTo)} aria-label={`${host.serverCount} assigned servers on ${host.name}`}>{host.serverCount}</a></td>
<td class:stale>{hostCpuUsage(host.inventory.cpuUsagePercent)}</td>
<td class:stale>{hostUsedSpace(host.inventory.memoryTotal, host.inventory.memoryAvailable)}</td>
<td class:stale>{hostUsedSpace(host.inventory.storageTotal, host.inventory.storageAvailable)}</td>
</tr>
{/each}</tbody>
</table></div>
<style>
.table-wrap { overflow-x:auto; border:1px solid var(--border-subtle); border-radius:var(--radius-md); }
table { width:100%; min-width:850px; border-collapse:collapse; text-align:left; font-size:var(--text-sm); }
th { padding:12px 16px; color:var(--text-secondary); background:var(--surface-sunken); font-weight:500; font-size:var(--text-xs); }
th span { display:block; font-weight:400; margin-top:3px; }
td { padding:16px; background:var(--surface); border-top:1px solid var(--border-subtle); font-variant-numeric:tabular-nums; }
tr:hover td { background:var(--surface-raised); }
a { color:var(--accent-primary-hover); } .name { color:var(--text-primary); font-weight:600; text-decoration:none; overflow-wrap:anywhere; }
.name:hover { color:var(--accent-primary-hover); text-decoration:underline; }
.status { max-width:330px; min-width:140px; }
.secondary,.reason { display:block; font-size:var(--text-xs); color:var(--text-secondary); margin-top:6px; line-height:1.5; }
.reason { overflow-wrap:anywhere; text-decoration:none; } .reason:hover { text-decoration:underline; color:var(--accent-primary-hover); }
.stale { color:var(--text-secondary); }
a:focus-visible { outline:2px solid var(--accent-primary); outline-offset:3px; }
</style>