feat(web): label table cells for the mobile card stack
This commit is contained in:
@@ -77,18 +77,18 @@ export default function AuditPage() {
|
||||
<Tbody>
|
||||
{events.map((e: AuditEvent) => (
|
||||
<Tr key={e.id}>
|
||||
<Td>
|
||||
<Td label="Time">
|
||||
<span className="whitespace-nowrap font-mono text-xs text-text-secondary">
|
||||
{formatDate(e.created_at)}
|
||||
</span>
|
||||
</Td>
|
||||
<Td>
|
||||
<Td label="Event">
|
||||
<EventTypeBadge type={e.event_type} />
|
||||
</Td>
|
||||
<Td>
|
||||
<Td label="Actor">
|
||||
<span className="text-sm text-text-primary">{e.actor}</span>
|
||||
</Td>
|
||||
<Td>
|
||||
<Td label="Details">
|
||||
<span className="text-sm text-text-secondary">{e.details}</span>
|
||||
</Td>
|
||||
</Tr>
|
||||
|
||||
@@ -385,7 +385,7 @@ export default function KeyDetailPage() {
|
||||
<Tbody>
|
||||
{key.assignments.map((assignment) => (
|
||||
<Tr key={`${assignment.key_id}-${assignment.server_id}`}>
|
||||
<Td>
|
||||
<Td label="Server">
|
||||
<Link
|
||||
href={`/servers/${assignment.server_id}`}
|
||||
className="font-medium text-text-primary hover:text-accent"
|
||||
@@ -393,22 +393,22 @@ export default function KeyDetailPage() {
|
||||
{assignment.server?.hostname ?? assignment.server_id}
|
||||
</Link>
|
||||
</Td>
|
||||
<Td>
|
||||
<Td label="IP Address">
|
||||
<span className="font-mono text-xs text-text-secondary">
|
||||
{assignment.server?.ip_address ?? "n/a"}
|
||||
</span>
|
||||
</Td>
|
||||
<Td>
|
||||
<Td label="Status">
|
||||
<Badge variant={assignment.revoked_at ? "danger" : "success"}>
|
||||
{assignment.revoked_at ? "revoked" : "active"}
|
||||
</Badge>
|
||||
</Td>
|
||||
<Td>
|
||||
<Td label="Assigned">
|
||||
<span className="text-text-secondary text-xs">
|
||||
{new Date(assignment.assigned_at).toLocaleDateString()}
|
||||
</span>
|
||||
</Td>
|
||||
<Td>
|
||||
<Td label="Revoked">
|
||||
<span className="text-text-secondary text-xs">
|
||||
{assignment.revoked_at
|
||||
? new Date(assignment.revoked_at).toLocaleDateString()
|
||||
|
||||
@@ -146,21 +146,21 @@ export default function KeysPage() {
|
||||
<Tbody>
|
||||
{keys.map((key: Key) => (
|
||||
<Tr key={key.key_id}>
|
||||
<Td>
|
||||
<Td label="Label">
|
||||
<span className="font-medium text-text-primary">{key.label}</span>
|
||||
</Td>
|
||||
<Td>
|
||||
<Td label="Fingerprint">
|
||||
<span className="font-mono text-xs text-text-secondary">{key.fingerprint}</span>
|
||||
</Td>
|
||||
<Td>
|
||||
<Td label="Source">
|
||||
<Badge variant={key.source === "generated" ? "accent" : "neutral"}>{key.source}</Badge>
|
||||
</Td>
|
||||
<Td>
|
||||
<Td label="Assignments">
|
||||
<span className="text-text-secondary">
|
||||
{key.assigned_count ?? 0} server{(key.assigned_count ?? 0) !== 1 ? "s" : ""}
|
||||
</span>
|
||||
</Td>
|
||||
<Td>
|
||||
<Td label="Created">
|
||||
<span className="text-text-secondary text-xs">{new Date(key.created_at).toLocaleDateString()}</span>
|
||||
</Td>
|
||||
<Td>
|
||||
|
||||
@@ -180,17 +180,17 @@ export default function MonitorDetailPage() {
|
||||
<Tbody>
|
||||
{incidents.map((inc) => (
|
||||
<Tr key={inc.incident_id}>
|
||||
<Td>
|
||||
<Td label="Started">
|
||||
<span className="text-xs text-text-secondary">{new Date(inc.started_at).toLocaleString()}</span>
|
||||
</Td>
|
||||
<Td>
|
||||
<Td label="Resolved">
|
||||
{inc.resolved_at ? (
|
||||
<span className="text-xs text-text-secondary">{new Date(inc.resolved_at).toLocaleString()}</span>
|
||||
) : (
|
||||
<Badge variant="danger">ongoing</Badge>
|
||||
)}
|
||||
</Td>
|
||||
<Td>
|
||||
<Td label="Cause">
|
||||
<span className="text-xs text-text-primary">{inc.cause || "n/a"}</span>
|
||||
</Td>
|
||||
</Tr>
|
||||
|
||||
@@ -76,24 +76,24 @@ export default function MonitorsPage() {
|
||||
<Tbody>
|
||||
{monitors.map((m) => (
|
||||
<Tr key={m.monitor_id}>
|
||||
<Td>
|
||||
<Td label="Name">
|
||||
<Link href={`/monitors/${m.monitor_id}`} className="font-medium text-text-primary hover:text-accent">
|
||||
{m.name}
|
||||
</Link>
|
||||
</Td>
|
||||
<Td>
|
||||
<Td label="Type">
|
||||
<Badge variant="neutral">{m.type}</Badge>
|
||||
</Td>
|
||||
<Td>
|
||||
<Td label="Target">
|
||||
<span className="font-mono text-xs text-text-secondary">{targetSummary(m)}</span>
|
||||
</Td>
|
||||
<Td>
|
||||
<Td label="Status">
|
||||
<Badge variant={statusVariant(m.state.status)}>{m.state.status}</Badge>
|
||||
</Td>
|
||||
<Td>
|
||||
<Td label="Latency">
|
||||
<span className="text-sm text-text-secondary">{m.state.latency_ms}ms</span>
|
||||
</Td>
|
||||
<Td>
|
||||
<Td label="Last check">
|
||||
<span className="text-xs text-text-secondary">
|
||||
{m.state.last_check_at ? new Date(m.state.last_check_at).toLocaleTimeString() : "n/a"}
|
||||
</span>
|
||||
|
||||
@@ -138,11 +138,11 @@ function SecretRow({ group, secret }: { group: string; secret: Secret }) {
|
||||
|
||||
return (
|
||||
<Tr>
|
||||
<Td>
|
||||
<Td label="Key">
|
||||
<span className="font-mono font-medium text-text-primary">{secret.key}</span>
|
||||
</Td>
|
||||
<Td>{revealed == null ? <span className="font-mono text-text-tertiary">••••••••••••</span> : <span className="font-mono text-xs break-all text-text-primary">{revealed}</span>}</Td>
|
||||
<Td>
|
||||
<Td label="Value">{revealed == null ? <span className="font-mono text-text-tertiary">••••••••••••</span> : <span className="font-mono text-xs break-all text-text-primary">{revealed}</span>}</Td>
|
||||
<Td label="Updated">
|
||||
<span className="text-text-secondary text-xs">{new Date(secret.updated_at).toLocaleString()}</span>
|
||||
</Td>
|
||||
<Td>
|
||||
|
||||
@@ -139,15 +139,15 @@ export default function SecretsPage() {
|
||||
<Tbody>
|
||||
{groups.map((g: SecretGroupSummary) => (
|
||||
<Tr key={g.group}>
|
||||
<Td>
|
||||
<Td label="Group">
|
||||
<span className="font-mono font-medium text-text-primary">{g.group}</span>
|
||||
</Td>
|
||||
<Td>
|
||||
<Td label="Keys">
|
||||
<span className="text-text-secondary">
|
||||
{g.key_count} key{g.key_count !== 1 ? "s" : ""}
|
||||
</span>
|
||||
</Td>
|
||||
<Td>
|
||||
<Td label="Last Updated">
|
||||
<span className="text-text-secondary text-xs">
|
||||
{new Date(g.updated_at).toLocaleString()}
|
||||
</span>
|
||||
|
||||
@@ -269,13 +269,13 @@ function UpdatesModal({ updates, onClose, onApply, isApplying, applySuccess }: {
|
||||
<Tbody>
|
||||
{updates.map((u) => (
|
||||
<Tr key={u.name}>
|
||||
<Td>
|
||||
<Td label="Package">
|
||||
<span className="font-medium font-mono text-sm">{u.name}</span>
|
||||
</Td>
|
||||
<Td>
|
||||
<Td label="Current">
|
||||
<span className="font-mono text-xs text-text-secondary">{u.current_version || "n/a"}</span>
|
||||
</Td>
|
||||
<Td>
|
||||
<Td label="Available">
|
||||
<span className="font-mono text-xs text-success">{u.new_version}</span>
|
||||
</Td>
|
||||
</Tr>
|
||||
@@ -585,19 +585,19 @@ export default function ServerDetailPage() {
|
||||
.filter((a) => a.key)
|
||||
.map((assignment) => (
|
||||
<Tr key={assignment.key_id}>
|
||||
<Td>
|
||||
<Td label="Label">
|
||||
<span className="font-medium">{assignment.key.label}</span>
|
||||
</Td>
|
||||
<Td>
|
||||
<Td label="Fingerprint">
|
||||
<span className="font-mono text-xs text-text-secondary">{assignment.key.fingerprint}</span>
|
||||
</Td>
|
||||
<Td>
|
||||
<Td label="Source">
|
||||
<Badge variant={assignment.key.source === "generated" ? "accent" : "neutral"}>{assignment.key.source}</Badge>
|
||||
</Td>
|
||||
<Td>
|
||||
<Td label="Status">
|
||||
<Badge variant={assignment.revoked_at ? "danger" : "success"}>{assignment.revoked_at ? "revoked" : "active"}</Badge>
|
||||
</Td>
|
||||
<Td>
|
||||
<Td label="Assigned">
|
||||
<span className="text-text-secondary text-xs">{formatDate(assignment.assigned_at)}</span>
|
||||
</Td>
|
||||
<Td>
|
||||
|
||||
@@ -113,23 +113,23 @@ export default function ServersPage() {
|
||||
<Tbody>
|
||||
{servers.map((server: Server) => (
|
||||
<Tr key={server.server_id}>
|
||||
<Td>
|
||||
<Td label="Hostname">
|
||||
<span className="font-medium text-text-primary">
|
||||
{server.hostname}
|
||||
</span>
|
||||
</Td>
|
||||
<Td>
|
||||
<Td label="IP Address">
|
||||
<span className="font-mono text-text-secondary">
|
||||
{server.ip_address}
|
||||
</span>
|
||||
</Td>
|
||||
<Td>
|
||||
<Td label="OS">
|
||||
<span className="text-text-secondary">{server.os_info}</span>
|
||||
</Td>
|
||||
<Td>
|
||||
<Td label="Status">
|
||||
<StatusDot status={resolveStatus(server, latestVersion)} />
|
||||
</Td>
|
||||
<Td>
|
||||
<Td label="Last Seen">
|
||||
<span className="text-text-secondary">
|
||||
{server.last_seen
|
||||
? formatLastSeen(server.last_seen)
|
||||
|
||||
@@ -51,7 +51,7 @@ export default function WorkflowRunsPage() {
|
||||
<Tbody>
|
||||
{runs.map((r: WorkflowRun) => (
|
||||
<Tr key={r.run_id}>
|
||||
<Td>
|
||||
<Td label="Run">
|
||||
<Link
|
||||
href={`/workflows/${id}/runs/${r.run_id}`}
|
||||
className="font-mono text-text-primary hover:text-signal"
|
||||
@@ -59,12 +59,12 @@ export default function WorkflowRunsPage() {
|
||||
{r.run_id.slice(0, 8)}
|
||||
</Link>
|
||||
</Td>
|
||||
<Td>
|
||||
<Td label="Status">
|
||||
<Badge variant={statusVariant[r.status] ?? "neutral"}>{r.status}</Badge>
|
||||
</Td>
|
||||
<Td className="text-text-secondary">{new Date(r.started_at).toLocaleString()}</Td>
|
||||
<Td className="text-text-secondary">{r.triggered_by}</Td>
|
||||
<Td className="text-text-secondary">{r.server_runs.length}</Td>
|
||||
<Td label="Started" className="text-text-secondary">{new Date(r.started_at).toLocaleString()}</Td>
|
||||
<Td label="By" className="text-text-secondary">{r.triggered_by}</Td>
|
||||
<Td label="Servers" className="text-text-secondary">{r.server_runs.length}</Td>
|
||||
</Tr>
|
||||
))}
|
||||
</Tbody>
|
||||
|
||||
@@ -70,15 +70,15 @@ export default function WorkflowsPage() {
|
||||
<Tbody>
|
||||
{workflows.map((w: Workflow) => (
|
||||
<Tr key={w.workflow_id}>
|
||||
<Td>
|
||||
<Td label="Name">
|
||||
<span className="font-medium text-text-primary">{w.name}</span>
|
||||
</Td>
|
||||
<Td>
|
||||
<Td label="Targets">
|
||||
<span className="text-text-secondary">
|
||||
{w.target_server_ids.length} server{w.target_server_ids.length !== 1 ? "s" : ""}
|
||||
</span>
|
||||
</Td>
|
||||
<Td>
|
||||
<Td label="Steps">
|
||||
<span className="text-text-secondary">{w.steps.length}</span>
|
||||
</Td>
|
||||
<Td>
|
||||
|
||||
@@ -115,11 +115,11 @@ export function MembersCard() {
|
||||
const locked = isSelf || managedByHQ || (u.role === "owner" && !isOwner);
|
||||
return (
|
||||
<Tr key={u.user_id}>
|
||||
<Td>
|
||||
<Td label="Email">
|
||||
<span className="font-medium">{u.email}</span>
|
||||
{isSelf && <span className="ml-2 text-xs text-text-tertiary">(you)</span>}
|
||||
</Td>
|
||||
<Td>
|
||||
<Td label="Role">
|
||||
{locked ? (
|
||||
<Badge variant={roleVariant(u.role)}>{u.role}</Badge>
|
||||
) : (
|
||||
@@ -136,11 +136,11 @@ export function MembersCard() {
|
||||
</select>
|
||||
)}
|
||||
</Td>
|
||||
<Td>
|
||||
<Td label="Sign-in">
|
||||
<Badge variant="neutral">{u.auth_source === "oidc" ? "SSO" : u.auth_source === "hq" ? "Vantage HQ" : "Password"}</Badge>
|
||||
</Td>
|
||||
<Td className="text-text-secondary">{u.last_login ? new Date(u.last_login).toLocaleString() : "Never"}</Td>
|
||||
<Td className="text-right">
|
||||
<Td label="Last login" className="text-text-secondary">{u.last_login ? new Date(u.last_login).toLocaleString() : "Never"}</Td>
|
||||
<Td label="Actions" className="text-right">
|
||||
{managedByHQ ? (
|
||||
hqUrl ? (
|
||||
<a href={hqUrl} target="_blank" rel="noreferrer" className="text-xs text-text-secondary underline">
|
||||
|
||||
Reference in New Issue
Block a user