The Leverantörsfakturor table was 1133px wide at every desktop size while the content column is at most 960px (max-w-5xl minus px-8) and 948px on a 1280-wide laptop. With nowrap cells every column adds its widest header or cell to the table's minimum width, so the overflow-x-auto wrapper scrolled sideways: Leverantör collapsed to its header width (129px) and the Status chips were cut at the right edge, with the Godkänn column off screen. Measured with the real page rendered under /sandbox at 1280, 1366, 1440, 1536 and 1920 wide. The list carried two date columns plus "Kvar att betala" on top of what the customer invoice list shows, and #2091's always-visible sort control added ~18px to each of seven headers, which tipped an already tight budget over the column. Viewport breakpoints cannot help because the column is capped at 960px regardless of screen size. - Drop the Fakturadatum column from the list: förfaller is the payer's date and the default order, and the invoice date lives in the detail view (the customer invoice list has no invoice-date column either). The sort comparator keeps invoice_date as its tie-break; only the header goes. - Shorten the sv header "Kvar att betala" to "Kvar": the label was 163px for a column whose numbers need ~120px. - Leave a column-budget comment on the table and one sentence in the dry-table design rule, since there is no shared list component to fix: every page-level list hand-writes the overflow-x-auto wrapper, and the three overflow reports had three different causes. After the change the table measures 948/960px (equal to its wrapper) with worst-case data (16-char invoice numbers, seven-digit amounts, two chips on one row), and Leverantör keeps 142-154px even then. Fixes #2262 Claude-Session: https://claude.ai/code/session_015qgLgdt4mLmha1ZLFMwq1u 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:
@@ -97,7 +97,7 @@ Compact metric cards (e.g. dashboard tiles, salary KPI row) use `p-4`. Detail ca
|
||||
| Need | Component | Notes |
|
||||
|---|---|---|
|
||||
| Page title + action | `components/ui/page-header.tsx` `PageHeader` | Use this, not bespoke `<h1>` + `<p>` blocks. Drop the `description` prop when it just paraphrases the title. |
|
||||
| Data table, **page-level list** | `components/ui/dry-table.tsx` `TH_CLASS` / `TD_CLASS` on a plain `<table className="w-full border-collapse text-[13px]">`, rows `hover:bg-secondary/35` | The concept list table: borderless, straight on the panel, 13px rows, hairline heads. This is what every migrated list page uses. Add `tabular-nums` to numeric cells. Hover-revealed row controls use `HOVER_REVEAL_CLASS` from the same file, never a hand-rolled `opacity-0 group-hover:opacity-100` (coarse pointers never hover, so the control would be unreachable on touch). |
|
||||
| Data table, **page-level list** | `components/ui/dry-table.tsx` `TH_CLASS` / `TD_CLASS` on a plain `<table className="w-full border-collapse text-[13px]">`, rows `hover:bg-secondary/35` | The concept list table: borderless, straight on the panel, 13px rows, hairline heads. This is what every migrated list page uses. Add `tabular-nums` to numeric cells. Hover-revealed row controls use `HOVER_REVEAL_CLASS` from the same file, never a hand-rolled `opacity-0 group-hover:opacity-100` (coarse pointers never hover, so the control would be unreachable on touch). Budget the columns before adding one: the content column is at most 960px (948px on a 1280-wide laptop) at every desktop size, so a nowrap column that does not fit makes the wrapper scroll sideways and squeezes the flexible name column to its header width (#2125, #2262); viewport breakpoints cannot buy room, drop or shorten a column instead. |
|
||||
| Data table, **dialog or report view** | `components/ui/table.tsx` `Table / TableHeader / TableHead / TableRow / TableCell` | Header style is baked in: `text-[11px] font-medium uppercase tracking-wider text-muted-foreground`. Wrap in `<CardContent className="p-0">` when the table is a card's primary content. `TableCell` is `px-4 py-3` on `text-sm`, so a page-level list built from this primitive comes out ~15% taller with a different hover tint: use the dry-table row above instead. |
|
||||
| Status indicator | `components/ui/badge.tsx` `<Badge variant>` | Chips mark exceptions only: normal states (Aktiv, Bokförd, Betald-i-tid) render as muted text (`text-muted-foreground text-xs`); Badge is reserved for rows that deviate (Utkast, Förfallen, Ej bokförd). A table where every row carries the same chip is wrong. Variants: `default / secondary / success / warning / destructive / outline`. **Never** use raw Tailwind colors (`bg-blue-100`, `bg-emerald-500/10`, etc.) for status. Map status → variant via a small `Record` per feature. |
|
||||
| No-data state | `components/ui/empty-state.tsx` `EmptyState` | Don't hand-roll `<div className="flex flex-col items-center py-12">…</div>`. Preset variants exist (`EmptyCustomers`, `EmptyByraClients`). |
|
||||
|
||||
@@ -613,6 +613,14 @@ export default function SupplierInvoicesPage() {
|
||||
}
|
||||
/>
|
||||
) : (
|
||||
/* Column budget (#2262): the content column is at most 960px (max-w-5xl
|
||||
minus px-8) and 948px on a 1280-wide laptop, at every desktop size,
|
||||
so viewport breakpoints cannot buy room. Every nowrap column adds its
|
||||
widest header or cell to the table's minimum width; past the budget
|
||||
the wrapper scrolls sideways, Leverantör collapses to its header
|
||||
width and Status is cut at the edge. That is why the list carries
|
||||
one date (förfaller: the payer's date and the default order) and a
|
||||
short Kvar header; fakturadatum lives in the detail view. */
|
||||
<div className="overflow-x-auto">
|
||||
<table className="w-full border-collapse text-[13px]">
|
||||
<thead>
|
||||
@@ -633,15 +641,6 @@ export default function SupplierInvoicesPage() {
|
||||
sort={sort}
|
||||
onSort={updateSort}
|
||||
/>
|
||||
<SortableHeader
|
||||
label={t('th_invoice_date')}
|
||||
sortLabel={t('sort_by', { column: t('th_invoice_date') })}
|
||||
column="invoice_date"
|
||||
sort={sort}
|
||||
onSort={updateSort}
|
||||
className="hidden text-right md:table-cell"
|
||||
align="right"
|
||||
/>
|
||||
<SortableHeader
|
||||
label={t('th_due_date')}
|
||||
sortLabel={t('sort_by', { column: t('th_due_date') })}
|
||||
@@ -763,9 +762,6 @@ export default function SupplierInvoicesPage() {
|
||||
{inv.supplier_invoice_number}
|
||||
</Link>
|
||||
</td>
|
||||
<td className={cn(TD_CLASS, 'hidden whitespace-nowrap text-right tabular-nums text-muted-foreground md:table-cell')}>
|
||||
{formatDate(inv.invoice_date)}
|
||||
</td>
|
||||
<td className={cn(TD_CLASS, 'hidden whitespace-nowrap text-right tabular-nums text-muted-foreground sm:table-cell')}>
|
||||
{formatDate(inv.due_date)}
|
||||
</td>
|
||||
|
||||
@@ -803,7 +803,6 @@
|
||||
"th_arrival": "Arrival",
|
||||
"th_supplier": "Supplier",
|
||||
"th_invoice_number": "Invoice no.",
|
||||
"th_invoice_date": "Invoice date",
|
||||
"th_due_date": "Due",
|
||||
"th_amount": "Amount",
|
||||
"th_remaining": "Remaining",
|
||||
|
||||
+1
-2
@@ -803,10 +803,9 @@
|
||||
"th_arrival": "Ankomst",
|
||||
"th_supplier": "Leverantör",
|
||||
"th_invoice_number": "Fakturanr",
|
||||
"th_invoice_date": "Fakturadatum",
|
||||
"th_due_date": "Förfaller",
|
||||
"th_amount": "Belopp",
|
||||
"th_remaining": "Kvar att betala",
|
||||
"th_remaining": "Kvar",
|
||||
"th_status": "Status",
|
||||
"sort_by": "Sortera efter {column}",
|
||||
"status_registered": "Registrerad",
|
||||
|
||||
Reference in New Issue
Block a user