feat(invoices): let the invoice list read by row tone without the status column (#2353)

* feat(invoices): let the invoice list read by row tone without the status column

A user asked for green/yellow/red row tints in the fakturalista (#2215) so
the list can be read without the last column. The need is real; the tint
is not the answer the design system already gives: status colours are
data, not chrome (convention 12), and DECISIONS 2026-08-03 already ruled
out row tints for this list.

Settled rows (paid, cancelled, credited) now recede to muted text as a
whole row. Open rows keep the foreground and the overdue chip stays the
one marker on its row, so three row classes read at a glance with zero
new colour: grey is done, dark is waiting for money, dark with an ochre
chip is late. Works in dark mode and the brand themes for free and never
stacks with the hover or selection tints.

The tone lives in a pure lib helper (invoiceRowTone, tested) and one
Record in the page maps tone to class; a row tint or a left-edge bar is
a three-string swap there if the founder prefers colour.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LvMaHcTnwAfxzgYD1fGYX1

* docs(decisions): record the first-principles choices of the 2026-09-06 evening issue batch

Ten lines for #2215 #2351 #2218 #2220 #2312 #2184 #2293 #2239 #2331 #2332
(PRs #2353 to #2362), carried by this PR so the other nine branches do not
all touch DECISIONS.md.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

---------

Co-authored-by: Jakob Wennberg <311770904+jakobwennberg-oss@users.noreply.github.com>
Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
Jakob Wennberg
2026-09-06 21:24:28 +02:00
committed by GitHub
co-authored by Claude Fable 5.1 Jakob Wennberg
parent bf7773d74b
commit 0016732232
4 changed files with 104 additions and 0 deletions
+17
View File
@@ -43,6 +43,7 @@ import {
type InvoiceListSort,
type InvoiceListSortColumn,
} from '@/lib/invoices/invoice-list-sort'
import { invoiceRowTone, type InvoiceRowTone } from '@/lib/invoices/invoice-list-row-tone'
import { listContextKey, writeListContext } from '@/lib/navigation/list-context'
import {
ArrowDown,
@@ -157,6 +158,21 @@ function statusGroupOf(invoice: Invoice): StatusGroup {
return 'settled'
}
// Row tone (#2215): the list must read without the status column. Settled
// rows recede to muted text, open rows keep the foreground, and the overdue
// chip stays the one marker on its row (one status indicator per element;
// semantic colours stay data-only, convention 12). This Record is the swap
// surface if the founder prefers colour: a row tint is
// 'bg-success/[0.04]' / 'bg-warning/[0.05]' / 'bg-destructive/[0.05]'; a
// left-edge bar is '[&>td:first-child]:border-l-2 [&>td:first-child]:border-l-success'
// and its warning/destructive siblings. Nothing else in the row changes.
const ROW_TONE_CLASS: Record<InvoiceRowTone, string> = {
settled: 'text-muted-foreground',
open: '',
overdue: '',
none: '',
}
const TAB_LABEL_KEYS: Record<ListTab, string> = {
all: 'tab_all',
unpaid: 'tab_unpaid',
@@ -1004,6 +1020,7 @@ export default function InvoicesPage() {
<tr
className={cn(
'group cursor-pointer transition-colors duration-150 hover:bg-secondary/35',
ROW_TONE_CLASS[invoiceRowTone(invoice)],
selectedIds.has(invoice.id) && 'bg-secondary/40',
)}
onClick={() => {