mirror of
https://github.com/MeshCore-Beacon/beacon-web.git
synced 2026-10-22 21:11:23 +00:00
Open the analyzer from an observation and add a hash size column
Drops the Open analyzer and Copy link buttons from the expansion; the analyzer popup keeps its own copy link. Table rows butt up against each other now, so the whole strip is a click target.
This commit is contained in:
1 parent
1f3fb5b7c9
commit
b2adc5276f
10 files changed
+76
-78
No files matched your search
@@ -2,7 +2,6 @@ import { useCallback, useMemo } from "react";
|
||||
import type { PacketSummary } from "../../types/api";
|
||||
import { formatPropagation } from "../../lib/formatters";
|
||||
import { Timestamp } from "../../components/Timestamp";
|
||||
import { CopyLinkButton } from "../../components/CopyLinkButton";
|
||||
import { usePacketDetail } from "./usePacketDetail";
|
||||
import { ObservationTable } from "./ObservationTable";
|
||||
import { buildPacketPaths } from "../map/packet-path";
|
||||
@@ -35,10 +34,13 @@ export function PacketExpansion({ packet, onOpenAnalyzer, onViewPath, selectedOb
|
||||
// than showing a blank (0-row) skeleton while it loads.
|
||||
const noObservations = packet.observationCount === 0;
|
||||
const emptyState = <div className="text-[10px] text-text-dim py-2">No observations</div>;
|
||||
// null drops ?analyze, so a link copied while the drawer is open still restores just the row
|
||||
const copyParams = useCallback(
|
||||
() => ({ tab: "Packets", hash: packet.packetHash, analyze: null }),
|
||||
[packet.packetHash],
|
||||
// Picking an observation is the way into the analyzer — it opens on the one you clicked.
|
||||
const handleSelectObservation = useCallback(
|
||||
(id: number) => {
|
||||
onSelectObservation(id);
|
||||
onOpenAnalyzer();
|
||||
},
|
||||
[onSelectObservation, onOpenAnalyzer],
|
||||
);
|
||||
|
||||
return (
|
||||
@@ -53,9 +55,6 @@ export function PacketExpansion({ packet, onOpenAnalyzer, onViewPath, selectedOb
|
||||
<span>first <Timestamp value={packet.firstHeardAt} /></span>
|
||||
<span>last <Timestamp value={packet.lastHeardAt} /></span>
|
||||
<span>spread {formatPropagation(spread)}</span>
|
||||
<button type="button" onClick={onOpenAnalyzer} disabled={!ready} className={ACTION_BUTTON_CLASS}>
|
||||
Open analyzer
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
onClick={onViewPath}
|
||||
@@ -65,7 +64,6 @@ export function PacketExpansion({ packet, onOpenAnalyzer, onViewPath, selectedOb
|
||||
>
|
||||
View path on map
|
||||
</button>
|
||||
<CopyLinkButton params={copyParams} ariaLabel="Copy row link" />
|
||||
</div>
|
||||
|
||||
<div className="max-h-[360px] overflow-y-auto">
|
||||
@@ -91,7 +89,7 @@ export function PacketExpansion({ packet, onOpenAnalyzer, onViewPath, selectedOb
|
||||
) : data && data.observations.length === 0 ? (
|
||||
emptyState
|
||||
) : data ? (
|
||||
<ObservationTable observations={data.observations} selectedId={selectedObservationId} onSelect={onSelectObservation} />
|
||||
<ObservationTable observations={data.observations} selectedId={selectedObservationId} onSelect={handleSelectObservation} />
|
||||
) : null}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -13,6 +13,7 @@ export function PacketTableHeader() {
|
||||
<span>Route</span>
|
||||
<span>Obs</span>
|
||||
<span>Hops</span>
|
||||
<span>Hash Size</span>
|
||||
<span>Src → Dst</span>
|
||||
<span>IATA</span>
|
||||
<span className="text-right">Age</span>
|
||||
|
||||
@@ -19,7 +19,8 @@ interface PacketTableRowProps {
|
||||
// expansion instead, which frees the wide column for the packet's endpoints.
|
||||
export function PacketTableRow({ packet, expanded, isFresh, onToggle }: PacketTableRowProps) {
|
||||
// ?? not ||, so a legitimate 0-hop direct packet still shows its count
|
||||
const hopCount = packet.latestObserver?.pathLength?.hopCount;
|
||||
const pathLength = packet.latestObserver?.pathLength;
|
||||
const na = <span className="text-text-dim">n/a</span>;
|
||||
|
||||
return (
|
||||
<div
|
||||
@@ -54,14 +55,13 @@ export function PacketTableRow({ packet, expanded, isFresh, onToggle }: PacketTa
|
||||
{packet.scope && <ScopeTag>{packet.scope}</ScopeTag>}
|
||||
</span>
|
||||
<span className="font-mono text-text-muted">×{packet.observationCount}</span>
|
||||
<span className="font-mono text-text-muted">
|
||||
{hopCount ?? <span className="text-text-dim">n/a</span>}
|
||||
</span>
|
||||
<span className="font-mono text-text-muted">{pathLength?.hopCount ?? na}</span>
|
||||
<span className="font-mono text-text-muted">{pathLength?.hashSize ?? na}</span>
|
||||
<span className="min-w-0 overflow-hidden">
|
||||
<PacketEndpoints packet={packet} />
|
||||
</span>
|
||||
<span className="font-mono font-bold text-primary tracking-wider">
|
||||
{packet.latestObserver?.iata ?? <span className="text-text-dim font-normal">n/a</span>}
|
||||
{packet.latestObserver?.iata ?? <span className="font-normal">{na}</span>}
|
||||
</span>
|
||||
<span className="text-right text-text-muted">
|
||||
<Timestamp value={packet.lastHeardAt} />
|
||||
|
||||
@@ -55,7 +55,7 @@ export function PacketVirtualList({
|
||||
count: packets.length,
|
||||
getScrollElement: () => parentRef.current,
|
||||
// a collapsed row: one grid line on desktop, a taller card below md. Expanded rows are remeasured.
|
||||
estimateSize: () => (isMobile ? 64 : 43),
|
||||
estimateSize: () => (isMobile ? 64 : 37),
|
||||
overscan: 10,
|
||||
getItemKey: (index) => packets[index]?.packetHash ?? index,
|
||||
});
|
||||
@@ -121,7 +121,8 @@ export function PacketVirtualList({
|
||||
transform: `translateY(${virtualRow.start}px)`,
|
||||
}}
|
||||
>
|
||||
<div className="pt-1.5">
|
||||
{/* cards need breathing room; table rows butt up so the whole strip is a click target */}
|
||||
<div className={isMobile ? "pt-1.5" : ""}>
|
||||
{isMobile ? (
|
||||
<PacketRow
|
||||
packet={packet}
|
||||
|
||||
@@ -2,4 +2,6 @@
|
||||
// track font- and content-independent: the header and the rows are separate grids, so a `ch` track
|
||||
// resolves against each one's own font size (9px vs 11px) and an `auto` track against its own text
|
||||
// ("HASH" vs "4AE77F09") — either silently drifts the two apart.
|
||||
export const GRID_TEMPLATE = "1.25rem 5rem 6rem 5rem 3rem 3rem minmax(6rem,1fr) 3.5rem 5rem";
|
||||
// Endpoints take minmax(0,1fr) so they absorb every bit of squeeze and truncate, rather than
|
||||
// pushing IATA and Age off the edge on a narrow viewport.
|
||||
export const GRID_TEMPLATE = "1.25rem 5rem 6rem 5rem 3rem 3rem 4rem minmax(0,1fr) 3.5rem 5rem";
|
||||
Reference in new issue
Block a user