* 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>
44 lines
2.2 KiB
TypeScript
44 lines
2.2 KiB
TypeScript
import { describe, it, expect } from 'vitest'
|
|
import { invoiceRowTone, type InvoiceRowToneInput } from '@/lib/invoices/invoice-list-row-tone'
|
|
|
|
function row(status: InvoiceRowToneInput['status'], extra: Partial<InvoiceRowToneInput> = {}): InvoiceRowToneInput {
|
|
return { status, credited_invoice_id: null, document_type: 'invoice', ...extra }
|
|
}
|
|
|
|
describe('invoiceRowTone', () => {
|
|
it('settled: paid, cancelled and credited rows recede regardless of document type', () => {
|
|
expect(invoiceRowTone(row('paid'))).toBe('settled')
|
|
expect(invoiceRowTone(row('cancelled'))).toBe('settled')
|
|
expect(invoiceRowTone(row('credited'))).toBe('settled')
|
|
expect(invoiceRowTone(row('paid', { credited_invoice_id: 'inv-1' }))).toBe('settled')
|
|
expect(invoiceRowTone(row('cancelled', { document_type: 'quote' }))).toBe('settled')
|
|
})
|
|
|
|
it('open: a sent or partially paid customer invoice is waiting for money', () => {
|
|
expect(invoiceRowTone(row('sent'))).toBe('open')
|
|
expect(invoiceRowTone(row('partially_paid'))).toBe('open')
|
|
})
|
|
|
|
it('overdue: an overdue customer invoice', () => {
|
|
expect(invoiceRowTone(row('overdue'))).toBe('overdue')
|
|
})
|
|
|
|
it('none: drafts and non-invoice documents never read as receivables', () => {
|
|
expect(invoiceRowTone(row('draft'))).toBe('none')
|
|
expect(invoiceRowTone(row('sent', { document_type: 'proforma' }))).toBe('none')
|
|
expect(invoiceRowTone(row('sent', { document_type: 'quote' }))).toBe('none')
|
|
expect(invoiceRowTone(row('sent', { document_type: 'delivery_note' }))).toBe('none')
|
|
expect(invoiceRowTone(row('overdue', { document_type: 'proforma' }))).toBe('none')
|
|
})
|
|
|
|
it('none: an unpaid credit note is not a receivable even when sent or overdue', () => {
|
|
expect(invoiceRowTone(row('sent', { credited_invoice_id: 'inv-1' }))).toBe('none')
|
|
expect(invoiceRowTone(row('overdue', { credited_invoice_id: 'inv-1' }))).toBe('none')
|
|
})
|
|
|
|
it('treats a missing or null document_type as a customer invoice, like the list page', () => {
|
|
expect(invoiceRowTone({ status: 'sent', credited_invoice_id: null })).toBe('open')
|
|
expect(invoiceRowTone({ status: 'overdue', credited_invoice_id: null, document_type: null })).toBe('overdue')
|
|
})
|
|
})
|