Give the operation badge its own column, and drop seq

"referenceYTD Sales" ran together because the badge shared the note column,
and that column had lost width to label and counts-toward. The badge is a
fixed-width token, so it gets a column of its own and stops competing with
free text.

seq goes with it. The label carries the sort order now -- it is typed with
its own "01 - " prefix -- so a separate ordinal column is one more thing to
keep in agreement with it for no gain. saveSeq and its state go too.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
Paul Trowbridge 2026-09-17 22:22:46 -04:00
parent af9e6de88e
commit 893a395529

View File

@ -141,7 +141,6 @@ export default function Baseline({ sources = [], sourceId, versions = [], versio
// are carried into the pivot as a "01 · " prefix, because Perspective orders
// column groups by the value string and no sort setting can express an
// arbitrary order.
const [seqs, setSeqs] = useState({})
const [bucketOrder, setBucketOrder] = useState([])
// Label and bucket are presentation, not definition: they change what the pivot
@ -180,22 +179,6 @@ export default function Baseline({ sources = [], sourceId, versions = [], versio
}
}
async function saveSeq(entry, value) {
const raw = String(value).trim()
const next = raw === '' ? null : parseInt(raw)
if (raw !== '' && !Number.isFinite(next)) { flash('Sequence must be a number', 'error'); return }
if (next === (entry.seq ?? null)) return
try {
const res = await fetch(`/api/log/${entry.id}`, {
method: 'PATCH',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ seq: next }),
})
if (!res.ok) { const d = await res.json(); flash(d.error, 'error'); return }
loadLog()
flash('Order saved')
} catch (err) { flash(err.message, 'error') }
}
async function saveBucketOrder(next) {
setBucketOrder(next)
@ -486,8 +469,8 @@ export default function Baseline({ sources = [], sourceId, versions = [], versio
<tr className="text-left text-gray-400 border-b border-gray-100">
<th className="px-3 py-1.5 font-medium w-6"></th>
<th className="px-3 py-1.5 font-medium">#</th>
<th className="px-3 py-1.5 font-medium w-14 text-right">seq</th>
<th className="px-3 py-1.5 font-medium w-40">label</th>
<th className="px-3 py-1.5 font-medium w-20">kind</th>
<th className="px-3 py-1.5 font-medium w-48">label</th>
<th className="px-3 py-1.5 font-medium">note</th>
<th className="px-3 py-1.5 font-medium w-36">counts toward</th>
<th className="px-3 py-1.5 font-medium text-right">rows</th>
@ -525,15 +508,13 @@ export default function Baseline({ sources = [], sourceId, versions = [], versio
>
<td className="px-3 py-2 text-gray-400 w-6"><span className="text-gray-300 text-xs">{isOpen ? '▾' : '▸'}</span></td>
<td className="px-3 py-2 text-gray-400">{log.length - i}</td>
<td className="px-3 py-2 text-right" onClick={e => e.stopPropagation()}>
<input
value={seqs[entry.id] ?? (entry.seq ?? '')}
onChange={e => setSeqs(v => ({ ...v, [entry.id]: e.target.value }))}
onBlur={e => saveSeq(entry, e.target.value)}
placeholder="—"
className="w-10 text-right border border-transparent hover:border-gray-200
focus:border-blue-400 rounded px-1 py-0.5 text-xs
focus:outline-none bg-transparent tabular-nums" />
{/* The operation badge gets its own column. Sharing one with
the note put "reference" hard against "YTD Sales" as soon
as the note column lost width to label and bucket. */}
<td className="px-3 py-2">
<span className={`inline-block px-1.5 py-0.5 rounded text-xs font-medium ${entry.operation === 'reference' ? 'bg-purple-50 text-purple-600' : 'bg-blue-50 text-blue-600'}`}>
{entry.operation}
</span>
</td>
<td className="px-3 py-2" onClick={e => e.stopPropagation()}>
<input
@ -546,9 +527,6 @@ export default function Baseline({ sources = [], sourceId, versions = [], versio
focus:outline-none bg-transparent" />
</td>
<td className="px-3 py-2">
<span className={`inline-block mr-2 px-1.5 py-0.5 rounded text-xs font-medium ${entry.operation === 'reference' ? 'bg-purple-50 text-purple-600' : 'bg-blue-50 text-blue-600'}`}>
{entry.operation}
</span>
{entry.note || <span className="text-gray-300"></span>}
</td>
<td className="px-3 py-2" onClick={e => e.stopPropagation()}>