Files
a92c492dbe refactor(ui): record detail pages as documents, not card piles (#1739)
Bring every record detail page onto the register-detail document grammar
from #1624 (DetailSection/DefRow, one status element per the list pages'
chips-mark-exceptions rule, one primary next step plus Förhandsgranska
visible and everything else behind a ⋯ overflow menu, line tables on the
dry-table idiom with the headline total in the serif):

- invoices/[id] (11 cards, 13-button toolbar): Kund | Detaljer rows,
  Fakturarader table + totals, Anteckningar, Betalning, Påminnelser,
  Utskickshistorik (InvoiceDeliveryHistory flattened); title carries the
  doc type, related documents become link rows
- supplier-invoices/[id], bookkeeping/[id] (serif title instead of
  font-mono, JournalEntryAttachments variant="section", CorrectionChain
  flattened), invoices/[id]/credit, assets/[id]/dispose (form as Fönster
  rows), salary employees/[id] (edit form behind Redigera in a dialog,
  Ingående saldon collapsed), salary runs/[id] + run panels (Betalfil,
  Skattebetalning, AGI, förmåner, override) and the payslip page
- DetailSection gains an optional help slot (convention 7)

Styling/structure only: no API, fetch, validation, state, dialog or
permission change; every action stays reachable.

Co-authored-by: Jakob Wennberg <311770904+jakobwennberg-oss@users.noreply.github.com>
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-08-20 12:29:43 +02:00

74 lines
2.1 KiB
TypeScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { cn } from '@/lib/utils'
/**
* Register-detail document grammar (customers, suppliers, articles).
*
* A detail page is one flowing document, not a pile of cards: each group of
* facts is introduced by an uppercase hairline kicker and set as aligned
* label/value rows. The kicker's hairline is the only rule; groups are
* separated by whitespace, never borders (Living Paper, design.md).
*/
export function DetailSection({
kicker,
help,
aside,
children,
className,
}: {
kicker: string
/** Section help behind a "?" right after the kicker (convention 7). Pass a <HelpPopover>. */
help?: React.ReactNode
/** Optional right-aligned element on the kicker line: a count, a quiet action. */
aside?: React.ReactNode
children: React.ReactNode
className?: string
}) {
return (
<section className={className}>
<div className="flex items-baseline justify-between gap-4 border-b border-border pb-2">
{/* data-ph-unmask: kickers are static i18n chrome in session replays. */}
<h2 data-ph-unmask="" className="flex items-center gap-2 text-[11px] font-medium uppercase tracking-wider text-muted-foreground">
{kicker}
{help}
</h2>
{aside}
</div>
<div className="mt-3">{children}</div>
</section>
)
}
export function DefRow({
label,
children,
className,
}: {
label: string
children: React.ReactNode
className?: string
}) {
return (
<div
className={cn(
'grid grid-cols-[8rem_1fr] gap-x-6 py-2 text-sm sm:grid-cols-[10rem_1fr]',
className,
)}
>
{/* data-ph-unmask on the label only: values (children) are user data
and stay masked in session replays. */}
<div data-ph-unmask="" className="text-muted-foreground">{label}</div>
<div className="min-w-0">{children}</div>
</div>
)
}
/**
* Muted placeholder for a value that matters but is not filled in. The en dash
* is the literal rendered value (same vocabulary as the orders list), which is
* why it is allowed to be a dash at all.
*/
export function DefEmpty() {
return <span className="text-muted-foreground">{'–'}</span>
}