diff --git a/ui/src/components/BridgeView.jsx b/ui/src/components/BridgeView.jsx index c959db8..c8d5c26 100644 --- a/ui/src/components/BridgeView.jsx +++ b/ui/src/components/BridgeView.jsx @@ -122,21 +122,36 @@ export function buildSteps(rows, { const isLoad = r.pf_iter === 'baseline' || r.pf_iter === 'reference' if (isLoad) { loads.value += v; loads.units += u; loads.rows += 1; continue } - const meta = logMeta[r.pf_logid] || {} + const meta = logMeta[r.pf_logid] || {} // label first, the same precedence pf_segment uses, so the bridge and the // pivot call a step by the same name - const tag = (meta.label || meta.tag || '').trim() - const label = tag || (meta.note || '').trim() || - `${(meta.operation || r.pf_iter || 'adj')}${r.pf_logid != null ? ` #${r.pf_logid}` : ''}` - const key = tag ? `tag:${tag}` : `log:${r.pf_logid}` - const g = byTag.get(key) || - { key, label, tagged: !!tag, value: 0, units: 0, rows: 0, logIds: new Set(), first: r.pf_logid } + const tag = (meta.label || meta.tag || '').trim() + // Untagged entries collapse into one step, the same as the ledger's + // "N untagged adjustments" line in OperationPanel.jsx -- one bar per + // untagged scale/recode would otherwise turn a routine cleanup pass into + // a wall of identical-looking bars. Tagged entries group case- + // insensitively for the same reason the ledger does: "Pull Forward" and + // "pull forward" are one initiative, not two, and the most-used spelling + // is what gets shown. + const key = tag ? `tag:${tag.toLowerCase()}` : 'untagged' + const g = byTag.get(key) || { + key, tagged: !!tag, value: 0, units: 0, rows: 0, logIds: new Set(), first: r.pf_logid, + spellings: tag ? new Map() : null, + } g.value += v; g.units += u; g.rows += 1 if (r.pf_logid != null) { g.logIds.add(r.pf_logid); g.first = Math.min(g.first ?? r.pf_logid, r.pf_logid) } + if (tag) g.spellings.set(tag, (g.spellings.get(tag) || 0) + 1) byTag.set(key, g) } - const mid = [...byTag.values()].sort((a, b) => (a.first ?? 0) - (b.first ?? 0)) + const mid = [...byTag.values()] + .sort((a, b) => (a.first ?? 0) - (b.first ?? 0)) + .map(g => ({ + ...g, + label: g.tagged + ? [...g.spellings.entries()].sort((a, b) => b[1] - a[1])[0]?.[0] || '' + : `${g.logIds.size} untagged adjustment${g.logIds.size === 1 ? '' : 's'}`, + })) const useBasis = !!basis && basisT.rows > 0 const out = [] @@ -534,17 +549,14 @@ export default function BridgeView({ {ln} ))} - {/* below the label, however many lines it took */} - {!isAnchor && s.entries > 1 && ( + {/* below the label, however many lines it took. Skipped for the + untagged bucket -- its label already reads "N untagged + adjustments", so a ×N here would just repeat the count. */} + {!isAnchor && s.tagged && s.entries > 1 && ( ×{s.entries} )} - {!s.tagged && !isAnchor && ( - - untagged - - )} ) })} @@ -587,9 +599,7 @@ export default function BridgeView({ {steps.map(s => ( - - {s.label}{!s.tagged && s.kind === 'step' && · untagged} - + {s.label} {s.kind === 'anchor' ? fmt(s.end) : fmtSigned(s.delta)}