fix(expenses): "Vem betalade?" as a compact select in the Underlag rail (#2327)
The four two-line radio rows pushed the primary button below the fold in the narrow rail. The question is now a select at the shared control height with the chosen answer's one-line consequence under it; the list keeps label + help per option and is pinned to the trigger width so it wraps inside the rail instead of spilling over the document viewer. Claude-Session: https://claude.ai/code/session_01P8YsvPqjfGxGZUkGeBVUWQ 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:
co-authored by
Jakob Wennberg
Claude Fable 5.1
parent
b0b995c77c
commit
3e5670f978
@@ -23,6 +23,7 @@ import {
|
||||
DialogHeader,
|
||||
DialogTitle,
|
||||
} from '@/components/ui/dialog'
|
||||
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select'
|
||||
import { useToast } from '@/components/ui/use-toast'
|
||||
import {
|
||||
Inbox,
|
||||
@@ -3083,7 +3084,13 @@ export type PayerChoice = 'company' | 'unpaid' | ExpensePayer
|
||||
|
||||
const PAYER_ORDER: PayerChoice[] = ['company', 'owner', 'employee', 'unpaid']
|
||||
|
||||
export function PayerChoiceList({
|
||||
/**
|
||||
* The "Vem betalade?" control: a compact select so the rail keeps its
|
||||
* primary button above the fold, with the chosen answer's one-line
|
||||
* consequence under it. Each option in the list carries the same help so
|
||||
* the choice is made with the consequence visible, not after.
|
||||
*/
|
||||
export function PayerChoiceSelect({
|
||||
value,
|
||||
onChange,
|
||||
accountingMethod,
|
||||
@@ -3098,38 +3105,24 @@ export function PayerChoiceList({
|
||||
return (
|
||||
<div className="space-y-1.5">
|
||||
<p className="text-[13px] font-medium">{t('payer_question')}</p>
|
||||
<div role="radiogroup" aria-label={t('payer_question')} className="rounded-lg border border-border">
|
||||
{PAYER_ORDER.map((choice) => {
|
||||
const selected = choice === value
|
||||
return (
|
||||
<button
|
||||
key={choice}
|
||||
type="button"
|
||||
role="radio"
|
||||
aria-checked={selected}
|
||||
onClick={() => onChange(choice)}
|
||||
className={cn(
|
||||
'flex w-full items-start gap-3 border-b border-border px-3 py-2.5 text-left transition-colors duration-150 last:border-b-0 hover:bg-secondary/30 first:rounded-t-lg last:rounded-b-lg',
|
||||
selected && 'bg-secondary/40',
|
||||
)}
|
||||
>
|
||||
<span
|
||||
aria-hidden
|
||||
className={cn(
|
||||
'mt-0.5 flex h-4 w-4 shrink-0 items-center justify-center rounded-full border bg-background',
|
||||
selected ? 'border-primary' : 'border-input',
|
||||
)}
|
||||
>
|
||||
{selected && <span className="h-1.5 w-1.5 rounded-full bg-primary" />}
|
||||
</span>
|
||||
<span className="min-w-0">
|
||||
<span className="block text-[13px]">{t(`payer_${choice}`)}</span>
|
||||
<span className="block text-xs text-muted-foreground">{t(helpKey(choice))}</span>
|
||||
</span>
|
||||
</button>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
<Select value={value} onValueChange={(next) => onChange(next as PayerChoice)}>
|
||||
<SelectTrigger aria-label={t('payer_question')} className="h-9 text-[13px]">
|
||||
{/* Explicit children: the items render label + help, and the
|
||||
trigger must show the label alone. */}
|
||||
<SelectValue>{t(`payer_${value}`)}</SelectValue>
|
||||
</SelectTrigger>
|
||||
{/* Match the trigger width so the two-line options wrap inside the rail
|
||||
instead of spilling over the document viewer. */}
|
||||
<SelectContent align="start" className="w-[var(--radix-select-trigger-width)]">
|
||||
{PAYER_ORDER.map((choice) => (
|
||||
<SelectItem key={choice} value={choice} className="py-2">
|
||||
<span className="block text-[13px]">{t(`payer_${choice}`)}</span>
|
||||
<span className="block text-xs text-muted-foreground">{t(helpKey(choice))}</span>
|
||||
</SelectItem>
|
||||
))}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
<p className="text-xs text-muted-foreground">{t(helpKey(value))}</p>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -3689,7 +3682,7 @@ function FieldsRail({
|
||||
answer keeps BFL 5 kap 6-7 § intact (the underlag is booked
|
||||
as a verifikat, never forced into a supplier invoice), and the
|
||||
verifikat editor stays reachable below as the escape hatch. */}
|
||||
<PayerChoiceList value={payer} onChange={setPayer} accountingMethod={accountingMethod} />
|
||||
<PayerChoiceSelect value={payer} onChange={setPayer} accountingMethod={accountingMethod} />
|
||||
{payer === 'company' ? (
|
||||
<Button variant="default" size="sm" className="w-full" onClick={onMatchTransaction}>
|
||||
Matcha mot transaktion
|
||||
|
||||
Reference in New Issue
Block a user