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 // 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 // column groups by the value string and no sort setting can express an
// arbitrary order. // arbitrary order.
const [seqs, setSeqs] = useState({})
const [bucketOrder, setBucketOrder] = useState([]) const [bucketOrder, setBucketOrder] = useState([])
// Label and bucket are presentation, not definition: they change what the pivot // 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) { async function saveBucketOrder(next) {
setBucketOrder(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"> <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 w-6"></th>
<th className="px-3 py-1.5 font-medium">#</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-20">kind</th>
<th className="px-3 py-1.5 font-medium w-40">label</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">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 w-36">counts toward</th>
<th className="px-3 py-1.5 font-medium text-right">rows</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 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-gray-400">{log.length - i}</td>
<td className="px-3 py-2 text-right" onClick={e => e.stopPropagation()}> {/* The operation badge gets its own column. Sharing one with
<input the note put "reference" hard against "YTD Sales" as soon
value={seqs[entry.id] ?? (entry.seq ?? '')} as the note column lost width to label and bucket. */}
onChange={e => setSeqs(v => ({ ...v, [entry.id]: e.target.value }))} <td className="px-3 py-2">
onBlur={e => saveSeq(entry, e.target.value)} <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'}`}>
placeholder="—" {entry.operation}
className="w-10 text-right border border-transparent hover:border-gray-200 </span>
focus:border-blue-400 rounded px-1 py-0.5 text-xs
focus:outline-none bg-transparent tabular-nums" />
</td> </td>
<td className="px-3 py-2" onClick={e => e.stopPropagation()}> <td className="px-3 py-2" onClick={e => e.stopPropagation()}>
<input <input
@ -546,9 +527,6 @@ export default function Baseline({ sources = [], sourceId, versions = [], versio
focus:outline-none bg-transparent" /> focus:outline-none bg-transparent" />
</td> </td>
<td className="px-3 py-2"> <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>} {entry.note || <span className="text-gray-300"></span>}
</td> </td>
<td className="px-3 py-2" onClick={e => e.stopPropagation()}> <td className="px-3 py-2" onClick={e => e.stopPropagation()}>