feat: UX improvements — nav, reports tabs, dashboard alerts, transaction hints, settings layout
- Move Reports to Finans nav group and auto-expand Övrigt on its pages - Make report tabs horizontally scrollable with gradient fade on mobile - Surface deadlines and alerts above the fold on dashboard - Add dismissible categorization hint card on transactions page - Split settings company form into 4 separate Cards for scannability - Add monthly breakdown report, document upload zone, journal entry attachments - Add batch category selector, receipt document linking, invoice form improvements Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.6
parent
ba94f60d06
commit
d0c0d8a7d2
@@ -0,0 +1,108 @@
|
||||
'use client'
|
||||
|
||||
import { useState } from 'react'
|
||||
import { Button } from '@/components/ui/button'
|
||||
import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogDescription } from '@/components/ui/dialog'
|
||||
import { Progress } from '@/components/ui/progress'
|
||||
import type { TransactionCategory } from '@/types'
|
||||
|
||||
const expenseCategories: { value: TransactionCategory; label: string }[] = [
|
||||
{ value: 'expense_equipment', label: 'Utrustning' },
|
||||
{ value: 'expense_software', label: 'Programvara' },
|
||||
{ value: 'expense_travel', label: 'Resor' },
|
||||
{ value: 'expense_office', label: 'Kontor' },
|
||||
{ value: 'expense_marketing', label: 'Marknadsföring' },
|
||||
{ value: 'expense_professional_services', label: 'Konsulter' },
|
||||
{ value: 'expense_education', label: 'Utbildning' },
|
||||
{ value: 'expense_bank_fees', label: 'Bankavgift' },
|
||||
{ value: 'expense_card_fees', label: 'Kortavgift' },
|
||||
{ value: 'expense_currency_exchange', label: 'Valutaväxling' },
|
||||
{ value: 'expense_other', label: 'Övrigt' },
|
||||
]
|
||||
|
||||
const incomeCategories: { value: TransactionCategory; label: string }[] = [
|
||||
{ value: 'income_services', label: 'Tjänster' },
|
||||
{ value: 'income_products', label: 'Produkter' },
|
||||
{ value: 'income_other', label: 'Övrigt' },
|
||||
]
|
||||
|
||||
interface BatchCategorySelectorProps {
|
||||
open: boolean
|
||||
onOpenChange: (open: boolean) => void
|
||||
selectedCount: number
|
||||
onSelectCategory: (category: TransactionCategory) => void
|
||||
progress: { done: number; total: number } | null
|
||||
}
|
||||
|
||||
export default function BatchCategorySelector({
|
||||
open,
|
||||
onOpenChange,
|
||||
selectedCount,
|
||||
onSelectCategory,
|
||||
progress,
|
||||
}: BatchCategorySelectorProps) {
|
||||
const isProcessing = progress !== null
|
||||
|
||||
return (
|
||||
<Dialog open={open} onOpenChange={isProcessing ? undefined : onOpenChange}>
|
||||
<DialogContent className="max-w-md">
|
||||
<DialogHeader>
|
||||
<DialogTitle>
|
||||
{isProcessing
|
||||
? `Kategoriserar ${progress.done}/${progress.total}...`
|
||||
: `Kategorisera ${selectedCount} transaktioner`}
|
||||
</DialogTitle>
|
||||
<DialogDescription>
|
||||
{isProcessing
|
||||
? 'Vänta medan transaktionerna kategoriseras'
|
||||
: 'Välj en kategori som ska tillämpas på alla valda transaktioner'}
|
||||
</DialogDescription>
|
||||
</DialogHeader>
|
||||
|
||||
{isProcessing ? (
|
||||
<div className="py-4">
|
||||
<Progress value={(progress.done / progress.total) * 100} />
|
||||
<p className="text-sm text-muted-foreground mt-2 text-center">
|
||||
{progress.done} av {progress.total} klara
|
||||
</p>
|
||||
</div>
|
||||
) : (
|
||||
<div className="space-y-4 py-2">
|
||||
<div>
|
||||
<h4 className="text-sm font-medium text-muted-foreground mb-2">Kostnader</h4>
|
||||
<div className="grid grid-cols-2 gap-1.5">
|
||||
{expenseCategories.map((cat) => (
|
||||
<Button
|
||||
key={cat.value}
|
||||
variant="outline"
|
||||
size="sm"
|
||||
className="justify-start text-xs"
|
||||
onClick={() => onSelectCategory(cat.value)}
|
||||
>
|
||||
{cat.label}
|
||||
</Button>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<h4 className="text-sm font-medium text-muted-foreground mb-2">Intäkter</h4>
|
||||
<div className="grid grid-cols-2 gap-1.5">
|
||||
{incomeCategories.map((cat) => (
|
||||
<Button
|
||||
key={cat.value}
|
||||
variant="outline"
|
||||
size="sm"
|
||||
className="justify-start text-xs"
|
||||
onClick={() => onSelectCategory(cat.value)}
|
||||
>
|
||||
{cat.label}
|
||||
</Button>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user