link_pair bug

This commit is contained in:
2026-06-23 20:55:03 +05:30
parent 49a4b51d75
commit 9b1a263196
4 changed files with 115 additions and 22 deletions

View File

@@ -29,6 +29,9 @@ const POPUP_ASPECT_VAL: Record<PopupAspect, number> = {
type Filters = {
videos: Set<string>; // empty = all
sides: Set<string>; // empty = all
// Carriageway/lane the asset's site_id belongs to (LHS / RHS). Stays
// distinct from `sides`, which is left/right-of-vehicle.
lanes: Set<string>;
types: Set<"fixed" | "range">; // empty = all
names: Set<string>; // empty = all (asset_name)
};
@@ -402,6 +405,7 @@ function AppShell({
const [filters, setFilters] = useState<Filters>({
videos: new Set(),
sides: new Set(),
lanes: new Set(),
types: new Set(),
names: new Set(),
});
@@ -731,15 +735,18 @@ function AppShell({
const counts = useMemo(() => {
const byVideo = new Map<string, number>();
const bySide = new Map<string, number>();
const byLane = new Map<string, number>();
const byType = new Map<string, number>();
for (const a of assets) {
const vn = displayVideo(a.video_name);
byVideo.set(vn, (byVideo.get(vn) ?? 0) + 1);
const s = a.side ?? "(none)";
bySide.set(s, (bySide.get(s) ?? 0) + 1);
const l = a.lane_side ?? "(none)";
byLane.set(l, (byLane.get(l) ?? 0) + 1);
byType.set(a.asset_type, (byType.get(a.asset_type) ?? 0) + 1);
}
return { byVideo, bySide, byType };
return { byVideo, bySide, byLane, byType };
}, [assets]);
const filteredAssets = useMemo(() => {
@@ -750,6 +757,10 @@ function AppShell({
const s = a.side ?? "(none)";
if (!filters.sides.has(s)) return false;
}
if (filters.lanes.size) {
const l = a.lane_side ?? "(none)";
if (!filters.lanes.has(l)) return false;
}
if (filters.types.size && !filters.types.has(a.asset_type)) return false;
if (filters.names.size && !filters.names.has(a.asset_name)) return false;
// Video filter: explicit list. If allVideoNames is loaded and the asset's
@@ -781,11 +792,15 @@ function AppShell({
const s = a.side ?? "(none)";
if (!filters.sides.has(s)) continue;
}
if (filters.lanes.size) {
const l = a.lane_side ?? "(none)";
if (!filters.lanes.has(l)) continue;
}
if (filters.types.size && !filters.types.has(a.asset_type)) continue;
m.set(a.asset_name, (m.get(a.asset_name) ?? 0) + 1);
}
return m;
}, [assets, filters.videos, filters.sides, filters.types]);
}, [assets, filters.videos, filters.sides, filters.lanes, filters.types]);
function toggleSet<T>(set: Set<T>, value: T): Set<T> {
const next = new Set(set);
@@ -798,6 +813,7 @@ function AppShell({
setFilters({
videos: new Set(),
sides: new Set(),
lanes: new Set(),
types: new Set(),
names: new Set(),
});
@@ -4250,6 +4266,7 @@ function AppShell({
disabled={
filters.videos.size +
filters.sides.size +
filters.lanes.size +
filters.types.size +
filters.names.size ===
0
@@ -4358,6 +4375,28 @@ function AppShell({
</label>
))}
<h4 title="Carriageway derived from the import-time site_id pair (lower site_id → LHS, higher → RHS)">
Lane
</h4>
{Array.from(counts.byLane.entries())
.sort()
.map(([lane, n]) => (
<label key={lane} className="filter-row">
<input
type="checkbox"
checked={filters.lanes.has(lane)}
onChange={() =>
setFilters((f) => ({
...f,
lanes: toggleSet(f.lanes, lane),
}))
}
/>
<span className="label">{lane}</span>
<span className="count">{n}</span>
</label>
))}
<h4
style={{ cursor: "pointer", userSelect: "none" }}
onClick={() => setVideosExpanded((v) => !v)}

View File

@@ -38,6 +38,7 @@ export type Asset = {
merged_into: number | null;
in_scope: number | null;
side: string | null;
lane_side: string | null;
link_pair_id: number | null;
link_locked: number | null;
};