Mobile view fixes (#188)
* fix: make new user checklist mobile responsive Adjust spacing, padding, typography, and indentation for small screens so the onboarding welcome screen works well on mobile devices. * fix: make expenses and bookkeeping verification views mobile responsive - Expenses/new: stack form grids on mobile, replace line items table with card layout, full-width action buttons - Expenses/[id]: stack header and actions, card layout for line items and payments, single-column info grid on mobile - JournalEntryList: unify verification lines into card layout for all screen sizes, hide redundant line descriptions that duplicate account name or entry description, stack filter bar and action buttons on mobile * fix: remove stray )); in provider_consents migration Syntax error on line 35 caused MIGRATIONS_FAILED on the Supabase staging branch. * fix: enhance mobile responsiveness for invoices page and update dialog styles
This commit is contained in:
@@ -170,13 +170,13 @@ export default function ExpenseDetailPage() {
|
||||
return (
|
||||
<div className="space-y-6 max-w-4xl">
|
||||
{/* Header */}
|
||||
<div className="flex items-center justify-between">
|
||||
<div className="flex items-center gap-4">
|
||||
<Button variant="ghost" size="icon" onClick={() => router.push('/expenses')}>
|
||||
<div className="space-y-4">
|
||||
<div className="flex items-start gap-4">
|
||||
<Button variant="ghost" size="icon" className="shrink-0 mt-1" onClick={() => router.push('/expenses')}>
|
||||
<ArrowLeft className="h-5 w-5" />
|
||||
</Button>
|
||||
<div>
|
||||
<div className="flex items-center gap-3">
|
||||
<div className="min-w-0">
|
||||
<div className="flex flex-wrap items-center gap-2 sm:gap-3">
|
||||
<h1 className="font-display text-2xl md:text-3xl font-medium tracking-tight">
|
||||
Utgift #{invoice.arrival_number}
|
||||
</h1>
|
||||
@@ -184,39 +184,40 @@ export default function ExpenseDetailPage() {
|
||||
{status.label}
|
||||
</Badge>
|
||||
</div>
|
||||
<p className="text-muted-foreground">
|
||||
<p className="text-muted-foreground truncate">
|
||||
{invoice.supplier?.name} · Faktura {invoice.supplier_invoice_number}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Context-aware actions */}
|
||||
<div className="flex gap-2">
|
||||
<div className="flex flex-col sm:flex-row gap-2 sm:justify-end">
|
||||
{invoice.status === 'registered' && (
|
||||
<>
|
||||
<Button onClick={handleApprove} disabled={isProcessing}>
|
||||
<Button className="w-full sm:w-auto" onClick={handleApprove} disabled={isProcessing}>
|
||||
<CheckCircle className="mr-2 h-4 w-4" />
|
||||
Godkänn
|
||||
</Button>
|
||||
<Button variant="destructive" size="icon" onClick={handleDelete} disabled={isProcessing}>
|
||||
<Trash2 className="h-4 w-4" />
|
||||
<Button variant="destructive" className="w-full sm:w-auto" onClick={handleDelete} disabled={isProcessing}>
|
||||
<Trash2 className="mr-2 h-4 w-4 sm:mr-0" />
|
||||
<span className="sm:hidden">Ta bort</span>
|
||||
</Button>
|
||||
</>
|
||||
)}
|
||||
{['approved', 'overdue'].includes(invoice.status) && (
|
||||
<>
|
||||
<Button onClick={() => setIsPayDialogOpen(true)} disabled={isProcessing}>
|
||||
<Button className="w-full sm:w-auto" onClick={() => setIsPayDialogOpen(true)} disabled={isProcessing}>
|
||||
<CreditCard className="mr-2 h-4 w-4" />
|
||||
Markera betald
|
||||
</Button>
|
||||
<Button variant="outline" onClick={handleCredit} disabled={isProcessing}>
|
||||
<Button variant="outline" className="w-full sm:w-auto" onClick={handleCredit} disabled={isProcessing}>
|
||||
<FileText className="mr-2 h-4 w-4" />
|
||||
Kreditfaktura
|
||||
</Button>
|
||||
</>
|
||||
)}
|
||||
{invoice.status === 'partially_paid' && (
|
||||
<Button onClick={() => setIsPayDialogOpen(true)} disabled={isProcessing}>
|
||||
<Button className="w-full sm:w-auto" onClick={() => setIsPayDialogOpen(true)} disabled={isProcessing}>
|
||||
<CreditCard className="mr-2 h-4 w-4" />
|
||||
Registrera betalning
|
||||
</Button>
|
||||
@@ -231,7 +232,7 @@ export default function ExpenseDetailPage() {
|
||||
</CardHeader>
|
||||
<CardContent className="space-y-6">
|
||||
{/* Info grid */}
|
||||
<div className="grid grid-cols-2 md:grid-cols-3 gap-4 text-sm">
|
||||
<div className="grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 gap-4 text-sm">
|
||||
<div>
|
||||
<span className="text-muted-foreground">Leverantör</span>
|
||||
<p className="font-medium">
|
||||
@@ -282,28 +283,51 @@ export default function ExpenseDetailPage() {
|
||||
{/* Line items */}
|
||||
{items.length > 0 && (
|
||||
<div className="border-t pt-4">
|
||||
<table className="w-full text-sm">
|
||||
<thead>
|
||||
<tr className="border-b text-left text-muted-foreground">
|
||||
<th className="pb-2">Beskrivning</th>
|
||||
<th className="pb-2 w-20">Konto</th>
|
||||
<th className="pb-2 w-16 text-right">Moms%</th>
|
||||
<th className="pb-2 w-28 text-right">Belopp</th>
|
||||
<th className="pb-2 w-24 text-right">Moms</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{items.map((item) => (
|
||||
<tr key={item.id} className="border-b last:border-0">
|
||||
<td className="py-2">{item.description}</td>
|
||||
<td className="py-2"><AccountNumber number={item.account_number} /></td>
|
||||
<td className="py-2 text-right">{Math.round(item.vat_rate * 100)}%</td>
|
||||
<td className="py-2 text-right font-mono">{formatAmount(item.line_total)}</td>
|
||||
<td className="py-2 text-right font-mono">{formatAmount(item.vat_amount)}</td>
|
||||
{/* Desktop: table */}
|
||||
<div className="hidden sm:block">
|
||||
<table className="w-full text-sm">
|
||||
<thead>
|
||||
<tr className="border-b text-left text-muted-foreground">
|
||||
<th className="pb-2">Beskrivning</th>
|
||||
<th className="pb-2 w-20">Konto</th>
|
||||
<th className="pb-2 w-16 text-right">Moms%</th>
|
||||
<th className="pb-2 w-28 text-right">Belopp</th>
|
||||
<th className="pb-2 w-24 text-right">Moms</th>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</thead>
|
||||
<tbody>
|
||||
{items.map((item) => (
|
||||
<tr key={item.id} className="border-b last:border-0">
|
||||
<td className="py-2">{item.description}</td>
|
||||
<td className="py-2"><AccountNumber number={item.account_number} /></td>
|
||||
<td className="py-2 text-right">{Math.round(item.vat_rate * 100)}%</td>
|
||||
<td className="py-2 text-right font-mono">{formatAmount(item.line_total)}</td>
|
||||
<td className="py-2 text-right font-mono">{formatAmount(item.vat_amount)}</td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
{/* Mobile: stacked cards */}
|
||||
<div className="sm:hidden space-y-3">
|
||||
{items.map((item) => (
|
||||
<div key={item.id} className="border rounded-lg p-3 space-y-2 text-sm">
|
||||
<div className="flex justify-between items-start gap-2">
|
||||
<span className="font-medium">{item.description || 'Ingen beskrivning'}</span>
|
||||
<AccountNumber number={item.account_number} />
|
||||
</div>
|
||||
<div className="flex justify-between text-muted-foreground">
|
||||
<span>Belopp</span>
|
||||
<span className="font-mono">{formatAmount(item.line_total)}</span>
|
||||
</div>
|
||||
<div className="flex justify-between text-muted-foreground">
|
||||
<span>Moms ({Math.round(item.vat_rate * 100)}%)</span>
|
||||
<span className="font-mono">{formatAmount(item.vat_amount)}</span>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
|
||||
{/* Amounts summary */}
|
||||
<div className="mt-4 pt-4 border-t space-y-1 text-sm">
|
||||
@@ -351,32 +375,59 @@ export default function ExpenseDetailPage() {
|
||||
{payments.length > 0 && (
|
||||
<div>
|
||||
<p className="text-sm font-medium mb-2">Betalningshistorik</p>
|
||||
<table className="w-full text-sm">
|
||||
<thead>
|
||||
<tr className="border-b text-left text-muted-foreground">
|
||||
<th className="pb-2">Datum</th>
|
||||
<th className="pb-2 text-right">Belopp</th>
|
||||
<th className="pb-2">Verifikation</th>
|
||||
<th className="pb-2">Anteckning</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{payments.map((p) => (
|
||||
<tr key={p.id} className="border-b last:border-0">
|
||||
<td className="py-2">{p.payment_date}</td>
|
||||
<td className="py-2 text-right font-mono">{formatAmount(p.amount)} {p.currency}</td>
|
||||
<td className="py-2">
|
||||
{p.journal_entry_id ? (
|
||||
<Link href={`/bookkeeping/${p.journal_entry_id}`} className="text-primary hover:underline font-mono text-xs">
|
||||
{p.journal_entry_id.substring(0, 8)}...
|
||||
</Link>
|
||||
) : '-'}
|
||||
</td>
|
||||
<td className="py-2 text-muted-foreground">{p.notes || '-'}</td>
|
||||
|
||||
{/* Desktop: table */}
|
||||
<div className="hidden sm:block">
|
||||
<table className="w-full text-sm">
|
||||
<thead>
|
||||
<tr className="border-b text-left text-muted-foreground">
|
||||
<th className="pb-2">Datum</th>
|
||||
<th className="pb-2 text-right">Belopp</th>
|
||||
<th className="pb-2">Verifikation</th>
|
||||
<th className="pb-2">Anteckning</th>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</thead>
|
||||
<tbody>
|
||||
{payments.map((p) => (
|
||||
<tr key={p.id} className="border-b last:border-0">
|
||||
<td className="py-2">{p.payment_date}</td>
|
||||
<td className="py-2 text-right font-mono">{formatAmount(p.amount)} {p.currency}</td>
|
||||
<td className="py-2">
|
||||
{p.journal_entry_id ? (
|
||||
<Link href={`/bookkeeping/${p.journal_entry_id}`} className="text-primary hover:underline font-mono text-xs">
|
||||
{p.journal_entry_id.substring(0, 8)}...
|
||||
</Link>
|
||||
) : '-'}
|
||||
</td>
|
||||
<td className="py-2 text-muted-foreground">{p.notes || '-'}</td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
{/* Mobile: stacked cards */}
|
||||
<div className="sm:hidden space-y-3">
|
||||
{payments.map((p) => (
|
||||
<div key={p.id} className="border rounded-lg p-3 space-y-1 text-sm">
|
||||
<div className="flex justify-between items-center">
|
||||
<span className="text-muted-foreground">{p.payment_date}</span>
|
||||
<span className="font-mono font-medium">{formatAmount(p.amount)} {p.currency}</span>
|
||||
</div>
|
||||
{p.journal_entry_id && (
|
||||
<div className="flex justify-between items-center">
|
||||
<span className="text-muted-foreground">Verifikation</span>
|
||||
<Link href={`/bookkeeping/${p.journal_entry_id}`} className="text-primary hover:underline font-mono text-xs">
|
||||
{p.journal_entry_id.substring(0, 8)}...
|
||||
</Link>
|
||||
</div>
|
||||
)}
|
||||
{p.notes && (
|
||||
<p className="text-muted-foreground text-xs pt-1">{p.notes}</p>
|
||||
)}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
|
||||
@@ -360,7 +360,7 @@ export default function NewExpensePage() {
|
||||
<CardTitle className="text-lg">Faktura</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent className="space-y-4">
|
||||
<div className="grid grid-cols-2 gap-4">
|
||||
<div className="grid grid-cols-1 sm:grid-cols-2 gap-4">
|
||||
<div className="space-y-2">
|
||||
<Label>Leverantör *</Label>
|
||||
<Controller
|
||||
@@ -400,7 +400,7 @@ export default function NewExpensePage() {
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className="grid grid-cols-3 gap-4">
|
||||
<div className="grid grid-cols-1 sm:grid-cols-3 gap-4">
|
||||
<div className="space-y-2">
|
||||
<Label>Fakturadatum *</Label>
|
||||
<Input type="date" {...register('invoice_date')} />
|
||||
@@ -422,12 +422,13 @@ export default function NewExpensePage() {
|
||||
|
||||
{/* Section 2: Kontering */}
|
||||
<Card>
|
||||
<CardHeader className="flex flex-row items-center justify-between">
|
||||
<CardHeader className="flex flex-col sm:flex-row sm:items-center justify-between gap-3">
|
||||
<CardTitle className="text-lg">Kontering</CardTitle>
|
||||
<Button
|
||||
type="button"
|
||||
variant="outline"
|
||||
size="sm"
|
||||
className="w-full sm:w-auto"
|
||||
onClick={() =>
|
||||
append({ description: '', amount: 0, account_number: '', vat_rate: 0.25 })
|
||||
}
|
||||
@@ -437,48 +438,143 @@ export default function NewExpensePage() {
|
||||
</Button>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<table className="w-full text-sm">
|
||||
<thead>
|
||||
<tr className="border-b text-left text-muted-foreground">
|
||||
<th className="pb-2 w-28">Konto</th>
|
||||
<th className="pb-2">Beskrivning</th>
|
||||
<th className="pb-2 w-32">Belopp (exkl.)</th>
|
||||
<th className="pb-2 w-24">Momssats</th>
|
||||
<th className="pb-2 w-24 text-right">Moms</th>
|
||||
<th className="pb-2 w-8"></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{fields.map((field, index) => (
|
||||
<tr key={field.id} className="border-b last:border-0 align-top">
|
||||
<td className="py-2 pr-2">
|
||||
<Controller
|
||||
name={`items.${index}.account_number`}
|
||||
control={control}
|
||||
render={({ field: f }) => (
|
||||
<AccountCombobox
|
||||
value={f.value}
|
||||
accounts={accounts}
|
||||
onChange={(val) => handleAccountChange(index, val)}
|
||||
/>
|
||||
{/* Desktop: table layout */}
|
||||
<div className="hidden sm:block">
|
||||
<table className="w-full text-sm">
|
||||
<thead>
|
||||
<tr className="border-b text-left text-muted-foreground">
|
||||
<th className="pb-2 w-28">Konto</th>
|
||||
<th className="pb-2">Beskrivning</th>
|
||||
<th className="pb-2 w-32">Belopp (exkl.)</th>
|
||||
<th className="pb-2 w-24">Momssats</th>
|
||||
<th className="pb-2 w-24 text-right">Moms</th>
|
||||
<th className="pb-2 w-8"></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{fields.map((field, index) => (
|
||||
<tr key={field.id} className="border-b last:border-0 align-top">
|
||||
<td className="py-2 pr-2">
|
||||
<Controller
|
||||
name={`items.${index}.account_number`}
|
||||
control={control}
|
||||
render={({ field: f }) => (
|
||||
<AccountCombobox
|
||||
value={f.value}
|
||||
accounts={accounts}
|
||||
onChange={(val) => handleAccountChange(index, val)}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
</td>
|
||||
<td className="py-2 pr-2">
|
||||
<Input
|
||||
placeholder="Beskrivning"
|
||||
{...register(`items.${index}.description`)}
|
||||
/>
|
||||
</td>
|
||||
<td className="py-2 pr-2">
|
||||
<Input
|
||||
type="number"
|
||||
step="0.01"
|
||||
placeholder="0,00"
|
||||
{...register(`items.${index}.amount`, { valueAsNumber: true })}
|
||||
/>
|
||||
</td>
|
||||
<td className="py-2 pr-2">
|
||||
<Controller
|
||||
name={`items.${index}.vat_rate`}
|
||||
control={control}
|
||||
render={({ field: f }) => (
|
||||
<Select
|
||||
value={String(f.value)}
|
||||
onValueChange={(v) => f.onChange(parseFloat(v))}
|
||||
>
|
||||
<SelectTrigger>
|
||||
<SelectValue />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem value="0.25">25%</SelectItem>
|
||||
<SelectItem value="0.12">12%</SelectItem>
|
||||
<SelectItem value="0.06">6%</SelectItem>
|
||||
<SelectItem value="0">0%</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
)}
|
||||
/>
|
||||
</td>
|
||||
<td className="py-2 pr-2 text-right font-mono pt-4">
|
||||
{formatAmount(itemTotals[index]?.vatAmount || 0)}
|
||||
</td>
|
||||
<td className="py-2 pt-3">
|
||||
{fields.length > 1 && (
|
||||
<Button
|
||||
type="button"
|
||||
variant="ghost"
|
||||
size="icon"
|
||||
onClick={() => remove(index)}
|
||||
>
|
||||
<Trash2 className="h-4 w-4 text-muted-foreground" />
|
||||
</Button>
|
||||
)}
|
||||
/>
|
||||
</td>
|
||||
<td className="py-2 pr-2">
|
||||
<Input
|
||||
placeholder="Beskrivning"
|
||||
{...register(`items.${index}.description`)}
|
||||
/>
|
||||
</td>
|
||||
<td className="py-2 pr-2">
|
||||
</td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
{/* Mobile: stacked card layout */}
|
||||
<div className="sm:hidden space-y-4">
|
||||
{fields.map((field, index) => (
|
||||
<div key={field.id} className="border rounded-lg p-3 space-y-3">
|
||||
<div className="flex items-center justify-between">
|
||||
<span className="text-sm font-medium text-muted-foreground">Rad {index + 1}</span>
|
||||
{fields.length > 1 && (
|
||||
<Button
|
||||
type="button"
|
||||
variant="ghost"
|
||||
size="icon"
|
||||
className="h-8 w-8"
|
||||
onClick={() => remove(index)}
|
||||
>
|
||||
<Trash2 className="h-4 w-4 text-muted-foreground" />
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
<div className="space-y-2">
|
||||
<Label className="text-xs text-muted-foreground">Konto</Label>
|
||||
<Controller
|
||||
name={`items.${index}.account_number`}
|
||||
control={control}
|
||||
render={({ field: f }) => (
|
||||
<AccountCombobox
|
||||
value={f.value}
|
||||
accounts={accounts}
|
||||
onChange={(val) => handleAccountChange(index, val)}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
<div className="space-y-2">
|
||||
<Label className="text-xs text-muted-foreground">Beskrivning</Label>
|
||||
<Input
|
||||
placeholder="Beskrivning"
|
||||
{...register(`items.${index}.description`)}
|
||||
/>
|
||||
</div>
|
||||
<div className="grid grid-cols-2 gap-3">
|
||||
<div className="space-y-2">
|
||||
<Label className="text-xs text-muted-foreground">Belopp (exkl.)</Label>
|
||||
<Input
|
||||
type="number"
|
||||
step="0.01"
|
||||
placeholder="0,00"
|
||||
{...register(`items.${index}.amount`, { valueAsNumber: true })}
|
||||
/>
|
||||
</td>
|
||||
<td className="py-2 pr-2">
|
||||
</div>
|
||||
<div className="space-y-2">
|
||||
<Label className="text-xs text-muted-foreground">Momssats</Label>
|
||||
<Controller
|
||||
name={`items.${index}.vat_rate`}
|
||||
control={control}
|
||||
@@ -499,40 +595,29 @@ export default function NewExpensePage() {
|
||||
</Select>
|
||||
)}
|
||||
/>
|
||||
</td>
|
||||
<td className="py-2 pr-2 text-right font-mono pt-4">
|
||||
{formatAmount(itemTotals[index]?.vatAmount || 0)}
|
||||
</td>
|
||||
<td className="py-2 pt-3">
|
||||
{fields.length > 1 && (
|
||||
<Button
|
||||
type="button"
|
||||
variant="ghost"
|
||||
size="icon"
|
||||
onClick={() => remove(index)}
|
||||
>
|
||||
<Trash2 className="h-4 w-4 text-muted-foreground" />
|
||||
</Button>
|
||||
)}
|
||||
</td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex justify-between items-center pt-1 border-t">
|
||||
<span className="text-xs text-muted-foreground">Moms</span>
|
||||
<span className="font-mono text-sm">{formatAmount(itemTotals[index]?.vatAmount || 0)} kr</span>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
|
||||
{/* Totals */}
|
||||
<div className="mt-4 pt-4 border-t space-y-2 text-right">
|
||||
<div className="flex justify-end gap-8">
|
||||
<div className="mt-4 pt-4 border-t space-y-2">
|
||||
<div className="flex justify-between sm:justify-end sm:gap-8">
|
||||
<span className="text-muted-foreground">Netto (exkl. moms)</span>
|
||||
<span className="font-mono w-32">{formatAmount(subtotal)} kr</span>
|
||||
<span className="font-mono sm:w-32 text-right">{formatAmount(subtotal)} kr</span>
|
||||
</div>
|
||||
<div className="flex justify-end gap-8">
|
||||
<div className="flex justify-between sm:justify-end sm:gap-8">
|
||||
<span className="text-muted-foreground">Moms</span>
|
||||
<span className="font-mono w-32">{formatAmount(totalVat)} kr</span>
|
||||
<span className="font-mono sm:w-32 text-right">{formatAmount(totalVat)} kr</span>
|
||||
</div>
|
||||
<div className="flex justify-end gap-8 font-bold text-lg">
|
||||
<div className="flex justify-between sm:justify-end sm:gap-8 font-bold text-lg">
|
||||
<span>Totalt</span>
|
||||
<span className="font-mono w-32">{formatAmount(total)} kr</span>
|
||||
<span className="font-mono sm:w-32 text-right">{formatAmount(total)} kr</span>
|
||||
</div>
|
||||
</div>
|
||||
</CardContent>
|
||||
@@ -551,7 +636,7 @@ export default function NewExpensePage() {
|
||||
</CardHeader>
|
||||
{advancedOpen && (
|
||||
<CardContent className="space-y-4 pt-0">
|
||||
<div className="grid grid-cols-2 gap-4">
|
||||
<div className="grid grid-cols-1 sm:grid-cols-2 gap-4">
|
||||
<div className="space-y-2">
|
||||
<Label>Valuta</Label>
|
||||
<Controller
|
||||
@@ -615,11 +700,11 @@ export default function NewExpensePage() {
|
||||
</Card>
|
||||
|
||||
{/* Submit */}
|
||||
<div className="flex justify-end gap-4">
|
||||
<Button type="button" variant="outline" onClick={() => router.push('/expenses')}>
|
||||
<div className="flex flex-col-reverse sm:flex-row sm:justify-end gap-3 sm:gap-4">
|
||||
<Button type="button" variant="outline" className="w-full sm:w-auto" onClick={() => router.push('/expenses')}>
|
||||
Avbryt
|
||||
</Button>
|
||||
<Button type="submit" disabled={isSubmitting}>
|
||||
<Button type="submit" disabled={isSubmitting} className="w-full sm:w-auto">
|
||||
{isSubmitting ? (
|
||||
<>
|
||||
<Loader2 className="mr-2 h-4 w-4 animate-spin" />
|
||||
|
||||
@@ -8,6 +8,7 @@ import { Card, CardContent } from '@/components/ui/card'
|
||||
import { Badge } from '@/components/ui/badge'
|
||||
import { Input } from '@/components/ui/input'
|
||||
import { Tabs, TabsList, TabsTrigger } from '@/components/ui/tabs'
|
||||
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select'
|
||||
import { PageHeader } from '@/components/ui/page-header'
|
||||
import { useToast } from '@/components/ui/use-toast'
|
||||
import { formatCurrency, formatDate } from '@/lib/utils'
|
||||
@@ -176,7 +177,23 @@ export default function InvoicesPage() {
|
||||
className="pl-10"
|
||||
/>
|
||||
</div>
|
||||
<Tabs value={activeTab} onValueChange={setActiveTab}>
|
||||
{/* Mobile: dropdown select */}
|
||||
<Select value={activeTab} onValueChange={setActiveTab}>
|
||||
<SelectTrigger className="sm:hidden w-full">
|
||||
<SelectValue />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem value="all">Alla</SelectItem>
|
||||
<SelectItem value="unpaid">Obetalda</SelectItem>
|
||||
<SelectItem value="paid">Betalda</SelectItem>
|
||||
<SelectItem value="draft">Utkast</SelectItem>
|
||||
<SelectItem value="proforma">Proforma</SelectItem>
|
||||
<SelectItem value="delivery_note">Följesedel</SelectItem>
|
||||
<SelectItem value="credit">Kredit</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
{/* Desktop: tab bar */}
|
||||
<Tabs value={activeTab} onValueChange={setActiveTab} className="hidden sm:block">
|
||||
<TabsList>
|
||||
<TabsTrigger value="all">Alla</TabsTrigger>
|
||||
<TabsTrigger value="unpaid">Obetalda</TabsTrigger>
|
||||
|
||||
Reference in New Issue
Block a user