feat: polish rod and trajectory UX interactions

Improve Majic Rod Solver usability with denser table controls, clearer unit-aware guidance, and stronger accessibility affordances for keyboard/screen-reader workflows.

Made-with: Cursor
This commit is contained in:
2026-04-17 08:16:11 -06:00
parent 2a6cee21f8
commit 64d4492c60
7 changed files with 163 additions and 19 deletions

View File

@@ -89,9 +89,23 @@ export function ResultsTab({
const fdm = result?.solvers?.fdm ?? primary ?? null;
const [overlayMode, setOverlayMode] = useState<OverlayMode>("depth");
const [selectedSegment, setSelectedSegment] = useState<number | null>(null);
const [traceabilityQuery, setTraceabilityQuery] = useState("");
const dlsUnitLabel = caseState.unitsSelection === 1 ? "deg/100 m" : "deg/100 ft";
const trajectorySegments = useMemo(() => buildTrajectorySegments(caseState.survey), [caseState.survey]);
const sideLoadProfile = primary?.profiles?.sideLoadProfile ?? null;
const pumpDiag = useMemo(() => computePumpPlacement(caseState), [caseState]);
const filteredTraceability = useMemo(() => {
const fields = result?.fieldTraceability?.fields ?? [];
const q = traceabilityQuery.trim().toLowerCase();
if (!q) return fields;
return fields.filter((row) => {
return (
row.xmlKey.toLowerCase().includes(q) ||
row.category.toLowerCase().includes(q) ||
row.notes.toLowerCase().includes(q)
);
});
}, [result?.fieldTraceability?.fields, traceabilityQuery]);
const dynacardData = useMemo<AlignedData | null>(() => {
const fdmSeries = toSeries(fdm ?? undefined);
@@ -240,8 +254,7 @@ export function ResultsTab({
</Fieldset>
<Fieldset legend="Trajectory Analytics">
<p className="panel-note" style={{ marginBottom: 8 }}>
Click a row or 3D segment to cross-highlight. DLS is shown numerically only (deg per
100 ft MD).
Click a row or 3D segment to cross-highlight. DLS is shown numerically only ({dlsUnitLabel}).
</p>
<div className="table-scroll" style={{ maxHeight: 250 }}>
<table className="data-table">
@@ -251,7 +264,7 @@ export function ResultsTab({
<th>MD start</th>
<th>MD end</th>
<th>ΔMD</th>
<th>DLS (deg/100)</th>
<th>DLS ({dlsUnitLabel})</th>
</tr>
</thead>
<tbody>
@@ -449,6 +462,18 @@ export function ResultsTab({
Categories mirror <code>docs/engineering/field-traceability.md</code> (
physics, metadata, parseCalibration, payloadInactive, parsedUnused).
</p>
<div className="button-row" style={{ gap: 8, alignItems: "center" }}>
<input
className="panel-input"
value={traceabilityQuery}
placeholder="Filter by XML key/category/notes"
aria-label="Filter traceability rows"
onChange={(e) => setTraceabilityQuery(e.target.value)}
/>
<span className="panel-note">
Showing {filteredTraceability.length} / {result.fieldTraceability.fields.length}
</span>
</div>
<div className="table-scroll" style={{ maxHeight: 280 }}>
<table className="data-table">
<thead>
@@ -456,14 +481,16 @@ export function ResultsTab({
<th>XML field</th>
<th>Category</th>
<th>In file</th>
<th>Notes</th>
</tr>
</thead>
<tbody>
{result.fieldTraceability.fields.map((row) => (
{filteredTraceability.map((row) => (
<tr key={row.xmlKey} title={row.notes || undefined}>
<td className="mono">{row.xmlKey}</td>
<td>{row.category}</td>
<td>{row.presentInXml ? "yes" : "—"}</td>
<td>{row.notes}</td>
</tr>
))}
</tbody>