From e775fb0e88fd0103ec642ce14f6dd463ffd961d7 Mon Sep 17 00:00:00 2001 From: Yashas Date: Wed, 7 Oct 2026 13:14:00 +0530 Subject: [PATCH] feat: the claims tile counts the lakehouse rows the link opens; live trace fills the screen MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Claims denied showed Ava's affected_claims (225) while "See the claims" opened 237 rows — her quantify query and the link's filters are two different queries. The tile now asks the record view for the link's own filter set (limit 1, pagination.total_count) and shows that; her figure appears underneath only when it disagrees. The URL→filter mapping moves to lib/claimsLink so the table and the tile share one definition. While a review runs the trace is the page: the layout widens to the screen, the list takes the viewport height, follows the newest step unless the viewer scrolled up, and shows a working row with a spinner and an indeterminate strip under the header. Reduced motion honoured. Co-Authored-By: Claude Fable 5.1 --- src/components/AvaLive.tsx | 47 ++++++++++++++++++++++++++++++++++-- src/components/Findings.tsx | 40 ++++++++++++++++++++++++++---- src/lib/claimsLink.ts | 42 ++++++++++++++++++++++++++++++++ src/screens/CaseScreen.tsx | 8 ++++-- src/screens/ClaimsScreen.tsx | 35 +++------------------------ src/styles.css | 32 ++++++++++++++++++++++++ 6 files changed, 164 insertions(+), 40 deletions(-) diff --git a/src/components/AvaLive.tsx b/src/components/AvaLive.tsx index 9f46055..a1005e8 100644 --- a/src/components/AvaLive.tsx +++ b/src/components/AvaLive.tsx @@ -1,4 +1,4 @@ -import { useState } from 'react'; +import { useEffect, useRef, useState } from 'react'; import { AlertTriangle, Brain, @@ -70,6 +70,7 @@ export function AvaLive({ steps, decisions, working, + fill = false, }: { steps: AvaTraceStep[]; decisions: AvaDecision[]; @@ -77,8 +78,27 @@ export function AvaLive({ * the "still working" footer, so an empty trace reads as "starting" rather * than "nothing happened". */ working: boolean; + /** While the review runs the trace is the page: let the list take the + * viewport instead of a fixed 32rem box. */ + fill?: boolean; }) { const [open, setOpen] = useState(null); + // Follow the run: when a new step lands while she is working, keep the + // newest step in view — unless the viewer has scrolled up to read. + const listRef = useRef(null); + const stuck = useRef(true); + useEffect(() => { + const el = listRef.current; + if (!el || !working || !stuck.current) return; + el.scrollTo({ top: el.scrollHeight, behavior: 'smooth' }); + }, [steps.length, working]); + function onScroll() { + const el = listRef.current; + if (!el) return; + stuck.current = el.scrollHeight - el.scrollTop - el.clientHeight < 48; + } + const last = steps[steps.length - 1]; + const sinceLast = last ? Math.max(0, (Date.now() - new Date(last.ended_at ?? last.started_at).getTime()) / 1000) : 0; const queries = steps.filter((s) => s.type === 'api'); const failed = queries.filter((s) => s.query_state === 'FAILED').length; @@ -112,6 +132,7 @@ export function AvaLive({ {spend > 0 && {money(spend.toFixed(2))}} + {working &&