small ahh change But Format
This commit is contained in:
1 parent
458123428e
commit
f52b7c18d9
12 files changed
+1409
-699
No files matched your search
+219
-70
@@ -1,12 +1,14 @@
|
||||
|
||||
import { useEffect, useMemo, useState } from "react";
|
||||
import { Link, useLocation, useNavigate } from "react-router-dom";
|
||||
// Utility for sorting
|
||||
const sorters = {
|
||||
name: (a: DeviceRow, b: DeviceRow) => a.name.localeCompare(b.name),
|
||||
ip: (a: DeviceRow, b: DeviceRow) => (a.ips[0] || "").localeCompare(b.ips[0] || ""),
|
||||
mac: (a: DeviceRow, b: DeviceRow) => (a.macs[0] || "").localeCompare(b.macs[0] || ""),
|
||||
presence: (a: DeviceRow, b: DeviceRow) => a.presence.localeCompare(b.presence),
|
||||
ip: (a: DeviceRow, b: DeviceRow) =>
|
||||
(a.ips[0] || "").localeCompare(b.ips[0] || ""),
|
||||
mac: (a: DeviceRow, b: DeviceRow) =>
|
||||
(a.macs[0] || "").localeCompare(b.macs[0] || ""),
|
||||
presence: (a: DeviceRow, b: DeviceRow) =>
|
||||
a.presence.localeCompare(b.presence),
|
||||
};
|
||||
|
||||
type SortKey = keyof typeof sorters;
|
||||
@@ -41,7 +43,12 @@ type WakeEvent = {
|
||||
};
|
||||
|
||||
const WAKE_HISTORY_KEY = "wakey_recent_wakes_v1";
|
||||
const PRESENCE_FILTERS: PresenceFilter[] = ["all", "online", "likely_online", "unknown"];
|
||||
const PRESENCE_FILTERS: PresenceFilter[] = [
|
||||
"all",
|
||||
"online",
|
||||
"likely_online",
|
||||
"unknown",
|
||||
];
|
||||
|
||||
function parseInventoryRows(payload: unknown): DeviceRow[] {
|
||||
if (!payload || typeof payload !== "object") return [];
|
||||
@@ -53,13 +60,20 @@ function parseInventoryRows(payload: unknown): DeviceRow[] {
|
||||
.map((raw, idx) => {
|
||||
if (!raw || typeof raw !== "object") return null;
|
||||
const device = raw as Record<string, unknown>;
|
||||
const names = Array.isArray(device.names) ? device.names.filter((v) => typeof v === "string") as string[] : [];
|
||||
const ips = Array.isArray(device.ips) ? device.ips.filter((v) => typeof v === "string") as string[] : [];
|
||||
const macs = Array.isArray(device.macs) ? device.macs.filter((v) => typeof v === "string") as string[] : [];
|
||||
const interfaces = Array.isArray(device.interfaces)
|
||||
? device.interfaces.filter((v) => typeof v === "string") as string[]
|
||||
const names = Array.isArray(device.names)
|
||||
? (device.names.filter((v) => typeof v === "string") as string[])
|
||||
: [];
|
||||
const presence = typeof device.presence === "string" ? device.presence : "unknown";
|
||||
const ips = Array.isArray(device.ips)
|
||||
? (device.ips.filter((v) => typeof v === "string") as string[])
|
||||
: [];
|
||||
const macs = Array.isArray(device.macs)
|
||||
? (device.macs.filter((v) => typeof v === "string") as string[])
|
||||
: [];
|
||||
const interfaces = Array.isArray(device.interfaces)
|
||||
? (device.interfaces.filter((v) => typeof v === "string") as string[])
|
||||
: [];
|
||||
const presence =
|
||||
typeof device.presence === "string" ? device.presence : "unknown";
|
||||
|
||||
const id = macs[0] || ips[0] || names[0] || `row-${idx}`;
|
||||
return {
|
||||
@@ -74,17 +88,24 @@ function parseInventoryRows(payload: unknown): DeviceRow[] {
|
||||
.filter((v): v is DeviceRow => Boolean(v));
|
||||
}
|
||||
|
||||
function parseWakeSummary(response: unknown): { outcome: string; requestId: string; detail: string } {
|
||||
function parseWakeSummary(response: unknown): {
|
||||
outcome: string;
|
||||
requestId: string;
|
||||
detail: string;
|
||||
} {
|
||||
if (!response || typeof response !== "object") {
|
||||
return { outcome: "error", requestId: "", detail: "invalid wake response" };
|
||||
}
|
||||
const envelope = response as Record<string, unknown>;
|
||||
const requestId = typeof envelope.request_id === "string" ? envelope.request_id : "";
|
||||
const status = typeof envelope.status === "string" ? envelope.status : "error";
|
||||
const requestId =
|
||||
typeof envelope.request_id === "string" ? envelope.request_id : "";
|
||||
const status =
|
||||
typeof envelope.status === "string" ? envelope.status : "error";
|
||||
|
||||
if (status !== "ok") {
|
||||
const error = envelope.error as Record<string, unknown> | undefined;
|
||||
const detail = typeof error?.message === "string" ? error.message : "wake failed";
|
||||
const detail =
|
||||
typeof error?.message === "string" ? error.message : "wake failed";
|
||||
return { outcome: "error", requestId, detail };
|
||||
}
|
||||
|
||||
@@ -102,24 +123,32 @@ function parseWakeSummary(response: unknown): { outcome: string; requestId: stri
|
||||
const status =
|
||||
typeof statusRaw === "string"
|
||||
? statusRaw
|
||||
: (typeof (statusRaw as Record<string, unknown> | undefined)?.kind === "string"
|
||||
? String((statusRaw as Record<string, unknown>).kind)
|
||||
: "unknown");
|
||||
: typeof (statusRaw as Record<string, unknown> | undefined)?.kind ===
|
||||
"string"
|
||||
? String((statusRaw as Record<string, unknown>).kind)
|
||||
: "unknown";
|
||||
const ip = typeof rowObj.ip === "string" ? rowObj.ip : "?";
|
||||
const mac = typeof rowObj.mac === "string" ? rowObj.mac : "?";
|
||||
return `${status}(${ip}/${mac})`;
|
||||
})
|
||||
.filter(Boolean);
|
||||
|
||||
const detail = targetOutcomes.length ? targetOutcomes.join(", ") : "wake dispatched";
|
||||
const failed = targetOutcomes.some((s) => s.startsWith("incomplete") || s.startsWith("nonexistent_address") || s.startsWith("wrong_size"));
|
||||
const detail = targetOutcomes.length
|
||||
? targetOutcomes.join(", ")
|
||||
: "wake dispatched";
|
||||
const failed = targetOutcomes.some(
|
||||
(s) =>
|
||||
s.startsWith("incomplete") ||
|
||||
s.startsWith("nonexistent_address") ||
|
||||
s.startsWith("wrong_size"),
|
||||
);
|
||||
return { outcome: failed ? "error" : "ok", requestId, detail };
|
||||
}
|
||||
|
||||
function chooseWakeTarget(device: DeviceRow): string {
|
||||
return device.name !== "(unnamed)"
|
||||
? device.name
|
||||
: (device.macs[0] || device.ips[0] || "");
|
||||
: device.macs[0] || device.ips[0] || "";
|
||||
}
|
||||
|
||||
function summarize(values: string[]): string {
|
||||
@@ -138,12 +167,12 @@ function loadHistory(): WakeEvent[] {
|
||||
if (!row || typeof row !== "object") return false;
|
||||
const event = row as Record<string, unknown>;
|
||||
return (
|
||||
typeof event.ts === "number"
|
||||
&& typeof event.target === "string"
|
||||
&& typeof event.agentId === "string"
|
||||
&& typeof event.outcome === "string"
|
||||
&& typeof event.requestId === "string"
|
||||
&& typeof event.detail === "string"
|
||||
typeof event.ts === "number" &&
|
||||
typeof event.target === "string" &&
|
||||
typeof event.agentId === "string" &&
|
||||
typeof event.outcome === "string" &&
|
||||
typeof event.requestId === "string" &&
|
||||
typeof event.detail === "string"
|
||||
);
|
||||
});
|
||||
} catch {
|
||||
@@ -152,10 +181,18 @@ function loadHistory(): WakeEvent[] {
|
||||
}
|
||||
|
||||
function saveHistory(history: WakeEvent[]) {
|
||||
window.localStorage.setItem(WAKE_HISTORY_KEY, JSON.stringify(history.slice(0, 20)));
|
||||
window.localStorage.setItem(
|
||||
WAKE_HISTORY_KEY,
|
||||
JSON.stringify(history.slice(0, 20)),
|
||||
);
|
||||
}
|
||||
|
||||
export function DevicesPage({ agents, selectedAgentId, onSelectAgent, onAfterWake }: Props) {
|
||||
export function DevicesPage({
|
||||
agents,
|
||||
selectedAgentId,
|
||||
onSelectAgent,
|
||||
onAfterWake,
|
||||
}: Props) {
|
||||
const location = useLocation();
|
||||
const navigate = useNavigate();
|
||||
|
||||
@@ -173,11 +210,16 @@ export function DevicesPage({ agents, selectedAgentId, onSelectAgent, onAfterWak
|
||||
function getPresenceFromUrl(): PresenceFilter {
|
||||
const params = new URLSearchParams(location.search);
|
||||
const raw = params.get("presence");
|
||||
return PRESENCE_FILTERS.includes(raw as PresenceFilter) ? (raw as PresenceFilter) : "all";
|
||||
return PRESENCE_FILTERS.includes(raw as PresenceFilter)
|
||||
? (raw as PresenceFilter)
|
||||
: "all";
|
||||
}
|
||||
|
||||
const [sort, setSort] = useState<{ key: SortKey; dir: SortDir }>(getSortFromUrl());
|
||||
const [presenceFilter, setPresenceFilter] = useState<PresenceFilter>(getPresenceFromUrl());
|
||||
const [sort, setSort] = useState<{ key: SortKey; dir: SortDir }>(
|
||||
getSortFromUrl(),
|
||||
);
|
||||
const [presenceFilter, setPresenceFilter] =
|
||||
useState<PresenceFilter>(getPresenceFromUrl());
|
||||
|
||||
// Keep view state in sync with URL
|
||||
useEffect(() => {
|
||||
@@ -221,7 +263,6 @@ export function DevicesPage({ agents, selectedAgentId, onSelectAgent, onAfterWak
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
useEffect(() => {
|
||||
setRecentWakes(loadHistory());
|
||||
}, []);
|
||||
@@ -257,7 +298,14 @@ export function DevicesPage({ agents, selectedAgentId, onSelectAgent, onAfterWak
|
||||
await wakeTarget(target, device.id);
|
||||
}
|
||||
|
||||
async function wakeTarget(target: string, busyId: string, opts: { refresh: boolean; notify: boolean } = { refresh: true, notify: true }) {
|
||||
async function wakeTarget(
|
||||
target: string,
|
||||
busyId: string,
|
||||
opts: { refresh: boolean; notify: boolean } = {
|
||||
refresh: true,
|
||||
notify: true,
|
||||
},
|
||||
) {
|
||||
if (!target || !selectedAgentId) return;
|
||||
setWakeBusyId(busyId);
|
||||
try {
|
||||
@@ -297,7 +345,10 @@ export function DevicesPage({ agents, selectedAgentId, onSelectAgent, onAfterWak
|
||||
for (const row of selectedRows) {
|
||||
const target = chooseWakeTarget(row);
|
||||
if (!target) continue;
|
||||
await wakeTarget(target, `bulk:${row.id}`, { refresh: false, notify: false });
|
||||
await wakeTarget(target, `bulk:${row.id}`, {
|
||||
refresh: false,
|
||||
notify: false,
|
||||
});
|
||||
}
|
||||
await loadInventory();
|
||||
if (onAfterWake) await onAfterWake();
|
||||
@@ -323,7 +374,9 @@ export function DevicesPage({ agents, selectedAgentId, onSelectAgent, onAfterWak
|
||||
}, [selectedAgentId]);
|
||||
|
||||
useEffect(() => {
|
||||
setSelectedIds((prev) => prev.filter((id) => rows.some((row) => row.id === id)));
|
||||
setSelectedIds((prev) =>
|
||||
prev.filter((id) => rows.some((row) => row.id === id)),
|
||||
);
|
||||
}, [rows]);
|
||||
|
||||
useEffect(() => {
|
||||
@@ -339,12 +392,13 @@ export function DevicesPage({ agents, selectedAgentId, onSelectAgent, onAfterWak
|
||||
result = result.filter((row) => row.presence === presenceFilter);
|
||||
}
|
||||
if (q) {
|
||||
result = result.filter((row) =>
|
||||
row.name.toLowerCase().includes(q)
|
||||
|| row.presence.toLowerCase().includes(q)
|
||||
|| row.ips.some((v) => v.toLowerCase().includes(q))
|
||||
|| row.macs.some((v) => v.toLowerCase().includes(q))
|
||||
|| row.interfaces.some((v) => v.toLowerCase().includes(q)),
|
||||
result = result.filter(
|
||||
(row) =>
|
||||
row.name.toLowerCase().includes(q) ||
|
||||
row.presence.toLowerCase().includes(q) ||
|
||||
row.ips.some((v) => v.toLowerCase().includes(q)) ||
|
||||
row.macs.some((v) => v.toLowerCase().includes(q)) ||
|
||||
row.interfaces.some((v) => v.toLowerCase().includes(q)),
|
||||
);
|
||||
}
|
||||
// Sort
|
||||
@@ -354,8 +408,11 @@ export function DevicesPage({ agents, selectedAgentId, onSelectAgent, onAfterWak
|
||||
return result;
|
||||
}, [rows, query, sort, presenceFilter]);
|
||||
|
||||
const selectedVisibleCount = filtered.filter((row) => selectedIds.includes(row.id)).length;
|
||||
const allVisibleSelected = filtered.length > 0 && selectedVisibleCount === filtered.length;
|
||||
const selectedVisibleCount = filtered.filter((row) =>
|
||||
selectedIds.includes(row.id),
|
||||
).length;
|
||||
const allVisibleSelected =
|
||||
filtered.length > 0 && selectedVisibleCount === filtered.length;
|
||||
|
||||
function toggleRowSelection(id: string, checked: boolean) {
|
||||
setSelectedIds((prev) => {
|
||||
@@ -366,7 +423,9 @@ export function DevicesPage({ agents, selectedAgentId, onSelectAgent, onAfterWak
|
||||
|
||||
function toggleAllVisible(checked: boolean) {
|
||||
if (!checked) {
|
||||
setSelectedIds((prev) => prev.filter((id) => !filtered.some((row) => row.id === id)));
|
||||
setSelectedIds((prev) =>
|
||||
prev.filter((id) => !filtered.some((row) => row.id === id)),
|
||||
);
|
||||
return;
|
||||
}
|
||||
setSelectedIds((prev) => {
|
||||
@@ -381,7 +440,10 @@ export function DevicesPage({ agents, selectedAgentId, onSelectAgent, onAfterWak
|
||||
<div className="card">
|
||||
<div className="row-head">
|
||||
<h2>Devices</h2>
|
||||
<button onClick={() => void loadInventory()} disabled={loading || !selectedAgentId}>
|
||||
<button
|
||||
onClick={() => void loadInventory()}
|
||||
disabled={loading || !selectedAgentId}
|
||||
>
|
||||
{loading ? "Refreshing..." : "Refresh"}
|
||||
</button>
|
||||
</div>
|
||||
@@ -394,7 +456,9 @@ export function DevicesPage({ agents, selectedAgentId, onSelectAgent, onAfterWak
|
||||
onChange={(e) => onSelectAgent(e.target.value)}
|
||||
required
|
||||
>
|
||||
<option value="" disabled>Select agent</option>
|
||||
<option value="" disabled>
|
||||
Select agent
|
||||
</option>
|
||||
{agents.map((agent) => (
|
||||
<option key={agent.agent_id} value={agent.agent_id}>
|
||||
{agent.agent_id} ({agent.connected ? "connected" : "offline"})
|
||||
@@ -423,13 +487,19 @@ export function DevicesPage({ agents, selectedAgentId, onSelectAgent, onAfterWak
|
||||
</label>
|
||||
<button
|
||||
onClick={() => void wakeTarget(quickWake.trim(), "quick")}
|
||||
disabled={!selectedAgentId || !quickWake.trim() || wakeBusyId === "quick"}
|
||||
disabled={
|
||||
!selectedAgentId || !quickWake.trim() || wakeBusyId === "quick"
|
||||
}
|
||||
>
|
||||
{wakeBusyId === "quick" ? "Waking..." : "Wake target"}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div className="presence-filters" role="group" aria-label="Filter by presence">
|
||||
<div
|
||||
className="presence-filters"
|
||||
role="group"
|
||||
aria-label="Filter by presence"
|
||||
>
|
||||
{PRESENCE_FILTERS.map((value) => (
|
||||
<button
|
||||
key={value}
|
||||
@@ -457,7 +527,9 @@ export function DevicesPage({ agents, selectedAgentId, onSelectAgent, onAfterWak
|
||||
disabled={!selectedVisibleCount || !selectedAgentId || bulkWakeBusy}
|
||||
type="button"
|
||||
>
|
||||
{bulkWakeBusy ? "Waking selected..." : `Wake selected (${selectedVisibleCount})`}
|
||||
{bulkWakeBusy
|
||||
? "Waking selected..."
|
||||
: `Wake selected (${selectedVisibleCount})`}
|
||||
</button>
|
||||
<button
|
||||
onClick={() => setSelectedIds([])}
|
||||
@@ -469,7 +541,9 @@ export function DevicesPage({ agents, selectedAgentId, onSelectAgent, onAfterWak
|
||||
{copyStatus && <span className="muted">{copyStatus}</span>}
|
||||
</div>
|
||||
|
||||
<p className="muted">Showing {filtered.length} of {rows.length}</p>
|
||||
<p className="muted">
|
||||
Showing {filtered.length} of {rows.length}
|
||||
</p>
|
||||
{error && <pre className="error">{error}</pre>}
|
||||
<div className="list device-list">
|
||||
<div className="row plain device-row device-header">
|
||||
@@ -484,27 +558,48 @@ export function DevicesPage({ agents, selectedAgentId, onSelectAgent, onAfterWak
|
||||
</span>
|
||||
<span
|
||||
className="sortable-col device-cell"
|
||||
onClick={() => setSort((s) => ({ key: "name", dir: s.key === "name" && s.dir === "asc" ? "desc" : "asc" }))}
|
||||
onClick={() =>
|
||||
setSort((s) => ({
|
||||
key: "name",
|
||||
dir: s.key === "name" && s.dir === "asc" ? "desc" : "asc",
|
||||
}))
|
||||
}
|
||||
>
|
||||
Name {sort.key === "name" ? (sort.dir === "asc" ? "▲" : "▼") : ""}
|
||||
</span>
|
||||
<span
|
||||
className="sortable-col device-cell"
|
||||
onClick={() => setSort((s) => ({ key: "ip", dir: s.key === "ip" && s.dir === "asc" ? "desc" : "asc" }))}
|
||||
onClick={() =>
|
||||
setSort((s) => ({
|
||||
key: "ip",
|
||||
dir: s.key === "ip" && s.dir === "asc" ? "desc" : "asc",
|
||||
}))
|
||||
}
|
||||
>
|
||||
IP {sort.key === "ip" ? (sort.dir === "asc" ? "▲" : "▼") : ""}
|
||||
</span>
|
||||
<span
|
||||
className="sortable-col device-cell"
|
||||
onClick={() => setSort((s) => ({ key: "mac", dir: s.key === "mac" && s.dir === "asc" ? "desc" : "asc" }))}
|
||||
onClick={() =>
|
||||
setSort((s) => ({
|
||||
key: "mac",
|
||||
dir: s.key === "mac" && s.dir === "asc" ? "desc" : "asc",
|
||||
}))
|
||||
}
|
||||
>
|
||||
MAC {sort.key === "mac" ? (sort.dir === "asc" ? "▲" : "▼") : ""}
|
||||
</span>
|
||||
<span
|
||||
className="sortable-col device-cell"
|
||||
onClick={() => setSort((s) => ({ key: "presence", dir: s.key === "presence" && s.dir === "asc" ? "desc" : "asc" }))}
|
||||
onClick={() =>
|
||||
setSort((s) => ({
|
||||
key: "presence",
|
||||
dir: s.key === "presence" && s.dir === "asc" ? "desc" : "asc",
|
||||
}))
|
||||
}
|
||||
>
|
||||
Presence {sort.key === "presence" ? (sort.dir === "asc" ? "▲" : "▼") : ""}
|
||||
Presence{" "}
|
||||
{sort.key === "presence" ? (sort.dir === "asc" ? "▲" : "▼") : ""}
|
||||
</span>
|
||||
<span className="device-cell">Interfaces</span>
|
||||
<span className="device-cell device-action">Actions</span>
|
||||
@@ -519,18 +614,63 @@ export function DevicesPage({ agents, selectedAgentId, onSelectAgent, onAfterWak
|
||||
aria-label={`Select ${row.name}`}
|
||||
/>
|
||||
</span>
|
||||
<span className="device-cell" data-label="Name" title={row.name}>{row.name}</span>
|
||||
<span className="device-cell muted" data-label="IP" title={row.ips.join(", ") || "-"}>{summarize(row.ips)}</span>
|
||||
<span className="device-cell muted" data-label="MAC" title={row.macs.join(", ") || "-"}>{summarize(row.macs)}</span>
|
||||
<span className="device-cell" data-label="Presence"><span className="pill">{row.presence}</span></span>
|
||||
<span className="device-cell" data-label="Interfaces" title={row.interfaces.join(", ") || "-"}>{summarize(row.interfaces)}</span>
|
||||
<span className="device-cell" data-label="Name" title={row.name}>
|
||||
{row.name}
|
||||
</span>
|
||||
<span
|
||||
className="device-cell muted"
|
||||
data-label="IP"
|
||||
title={row.ips.join(", ") || "-"}
|
||||
>
|
||||
{summarize(row.ips)}
|
||||
</span>
|
||||
<span
|
||||
className="device-cell muted"
|
||||
data-label="MAC"
|
||||
title={row.macs.join(", ") || "-"}
|
||||
>
|
||||
{summarize(row.macs)}
|
||||
</span>
|
||||
<span className="device-cell" data-label="Presence">
|
||||
<span className="pill">{row.presence}</span>
|
||||
</span>
|
||||
<span
|
||||
className="device-cell"
|
||||
data-label="Interfaces"
|
||||
title={row.interfaces.join(", ") || "-"}
|
||||
>
|
||||
{summarize(row.interfaces)}
|
||||
</span>
|
||||
<span className="device-cell device-action" data-label="">
|
||||
<button onClick={() => void wakeDevice(row)} disabled={wakeBusyId === row.id || !selectedAgentId || bulkWakeBusy}>
|
||||
<button
|
||||
onClick={() => void wakeDevice(row)}
|
||||
disabled={
|
||||
wakeBusyId === row.id || !selectedAgentId || bulkWakeBusy
|
||||
}
|
||||
>
|
||||
{wakeBusyId === row.id ? "Waking..." : "Wake"}
|
||||
</button>
|
||||
<button type="button" className="mini-btn" onClick={() => void copyValue("name", chooseWakeTarget(row))}>Copy name</button>
|
||||
<button type="button" className="mini-btn" onClick={() => void copyValue("ip", row.ips[0] || "")}>Copy IP</button>
|
||||
<button type="button" className="mini-btn" onClick={() => void copyValue("mac", row.macs[0] || "")}>Copy MAC</button>
|
||||
<button
|
||||
type="button"
|
||||
className="mini-btn"
|
||||
onClick={() => void copyValue("name", chooseWakeTarget(row))}
|
||||
>
|
||||
Copy name
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
className="mini-btn"
|
||||
onClick={() => void copyValue("ip", row.ips[0] || "")}
|
||||
>
|
||||
Copy IP
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
className="mini-btn"
|
||||
onClick={() => void copyValue("mac", row.macs[0] || "")}
|
||||
>
|
||||
Copy MAC
|
||||
</button>
|
||||
</span>
|
||||
</div>
|
||||
))}
|
||||
@@ -552,10 +692,15 @@ export function DevicesPage({ agents, selectedAgentId, onSelectAgent, onAfterWak
|
||||
</div>
|
||||
<div className="list">
|
||||
{recentWakes.map((event, idx) => (
|
||||
<div className="row plain" key={`${event.ts}-${event.target}-${idx}`}>
|
||||
<div
|
||||
className="row plain"
|
||||
key={`${event.ts}-${event.target}-${idx}`}
|
||||
>
|
||||
<div>
|
||||
<strong>{event.target}</strong>
|
||||
<div className="muted">{new Date(event.ts).toLocaleString()} on {event.agentId}</div>
|
||||
<div className="muted">
|
||||
{new Date(event.ts).toLocaleString()} on {event.agentId}
|
||||
</div>
|
||||
<div className="muted">{event.detail}</div>
|
||||
{event.requestId && (
|
||||
<Link
|
||||
@@ -566,14 +711,18 @@ export function DevicesPage({ agents, selectedAgentId, onSelectAgent, onAfterWak
|
||||
</Link>
|
||||
)}
|
||||
</div>
|
||||
<span className={`pill ${event.outcome === "ok" ? "ready" : "error"}`}>
|
||||
<span
|
||||
className={`pill ${event.outcome === "ok" ? "ready" : "error"}`}
|
||||
>
|
||||
{event.outcome}
|
||||
</span>
|
||||
</div>
|
||||
))}
|
||||
{!recentWakes.length && <div className="empty">No wake actions yet</div>}
|
||||
{!recentWakes.length && (
|
||||
<div className="empty">No wake actions yet</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user