Skip to content
File

Blob: ui/operator/Clients.svelte

4.4 KB
1<script lang="ts">
2 import Table from './Table.svelte';
3 import SnapshotTime from './SnapshotTime.svelte';
4 import type { ClientList } from './types';
5 
6 let { data }: { data: ClientList } = $props();
7 let expanded = $state<Record<string, boolean>>({});
8</script>
9 
10<p class="text-muted wrap-anywhere">Node <span class="ml-1 font-mono text-[0.92em] text-foreground">{data.node}</span></p>
11<div class="mt-6 mb-2.5 flex flex-wrap items-baseline justify-between gap-x-6 gap-y-1">
12 <h2 class="text-[15px] font-semibold">{data.clients.length} connected {data.clients.length === 1 ? 'client' : 'clients'}</h2>
13 <p class="text-[13px] text-muted">Observed <SnapshotTime value={data.observedAt} /></p>
14</div>
15{#if data.clients.length}
16 <Table label="Connected clients" tableClass="table-fixed min-[600px]:min-w-[42rem]">
17 <thead><tr><th scope="col" class="w-full min-[600px]:w-[74%]">Hostname / client</th><th scope="col" class="hidden min-[600px]:table-cell min-[600px]:w-[18%]">Peer address</th><th scope="col" class="hidden min-[600px]:table-cell min-[600px]:w-[8%]">Version</th></tr></thead>
18 <tbody>
19 {#each data.clients as client, index (`${client.identity}/${client.address}`)}
20 {@const key = `${client.identity}/${client.address}`}
21 {@const clientID = client.clientId}
22 <tr>
23 <th scope="row">
24 <div class="flex items-baseline gap-2">
25 {#if client.sessionMode}
26 <span class={['min-w-0 font-mono text-[0.92em]', client.sessionMode === 'ephemeral' && 'break-all']}>{client.hostname || `Client ${clientID}`}</span>
27 {:else}
28 <a class="-my-2 inline-flex min-h-10 min-w-0 items-center font-mono text-[0.92em] text-accent-300 underline underline-offset-4 hover:text-accent-100" href={client.url}>Client {clientID}</a>
29 {/if}
30 <span class="shrink-0 rounded-full border border-line bg-chrome px-2 py-0.5 text-xs leading-4 text-muted"><span class="sr-only">Type: </span>{client.sessionMode === 'ephemeral' ? 'Ephemeral' : client.sessionMode === 'token' ? 'Token' : 'Client'}</span>
31 </div>
32 <div class="mt-1 flex flex-wrap items-center gap-x-2 gap-y-1 text-[13px] text-muted">
33 {#if client.sessionMode === 'token' && client.ownerIdentity}
34 <span>Owner {#if client.ownerUrl}<a class="text-muted underline underline-offset-4 hover:text-foreground" href={client.ownerUrl}>{client.ownerLabel || client.ownerIdentity}</a>{:else}{client.ownerLabel || client.ownerIdentity}{/if}</span>
35 <span aria-hidden="true">·</span>
36 {/if}
37 <button
38 type="button"
39 class="-my-1.5 inline-flex min-h-9 shrink-0 items-center gap-1 whitespace-nowrap text-muted hover:text-foreground"
40 aria-expanded={Boolean(expanded[key])}
41 aria-controls={`client-identity-${index}`}
42 onclick={() => { expanded[key] = !expanded[key]; }}
43 ><span class="inline-block w-3 text-center" aria-hidden="true">{expanded[key] ? '▾' : '▸'}</span>Identity</button>
44 </div>
45 <div id={`client-identity-${index}`} hidden={!expanded[key]} class="mt-1 text-[13px] leading-relaxed text-muted">
46 <p><span class="mr-2">Connection identity</span><code class="font-mono text-[0.92em] break-all text-foreground">{client.identity}</code></p>
47 {#if client.ownerIdentity}<p class="mt-1"><span class="mr-2">Owner identity</span><code class="font-mono text-[0.92em] break-all text-foreground">{client.ownerIdentity}</code></p>{/if}
48 </div>
49 <div class="mt-2 flex flex-wrap gap-x-4 gap-y-1 text-xs text-muted min-[600px]:hidden">
50 <span>Peer address <span class="font-mono text-foreground">{client.address}</span></span>
51 <span>Version <span class="font-mono text-foreground">{client.version || 'Not reported'}</span></span>
52 </div>
53 </th>
54 <td class="hidden font-mono text-[0.92em] min-[600px]:table-cell">{client.address}</td>
55 <td class="hidden font-mono text-[0.92em] min-[600px]:table-cell">
56 {#if client.version}{client.version}{:else}<span class="font-sans text-muted">Not reported</span>{/if}
57 </td>
58 </tr>
59 {/each}
60 </tbody>
61 </Table>
62{:else}
63 <p class="border-y border-line py-6 text-muted">No clients connected to this node.</p>
64{/if}