Files
accounted/components/transactions/BatchCategorySelector.tsx
T
Jakob WennbergandClaude Opus 4.6 acb85edf4a feat: wire Zod validation into API routes, improve types and components
- Add 8 new Zod schemas (UpdateCustomer, UpdateSupplier, UpdateSupplierInvoice,
  UpdateAccount, BankUnlink, RunReconciliation, CorrectJournalEntry,
  EvaluateMappingRules) and wire validateBody() into 24 JSON-body API routes
- Remove redundant manual validation checks replaced by Zod
- Add comprehensive schema tests (222 tests)
- Improve type definitions in types/index.ts with expanded interfaces
- Refactor extension types (push-notifications, receipt-ocr) for cleaner imports
- Update transaction components (BatchCategorySelector, SwipeCategorizationView,
  QuickReviewDialog, VatTreatmentSelect) and invoice inbox workspace
- Add invoice-inbox utilities and type decoupling tests
- Fix NE-bilaga, SRU export, and invoice PDF template type usage
- Update CLAUDE.md with expanded architecture documentation

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-23 16:00:38 +01:00

107 lines
3.7 KiB
TypeScript

'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 VatTreatmentSelect from './VatTreatmentSelect'
import { EXPENSE_CATEGORIES, INCOME_CATEGORIES } from './transaction-types'
import type { TransactionCategory, VatTreatment } from '@/types'
const expenseCategories = EXPENSE_CATEGORIES
const incomeCategories = INCOME_CATEGORIES
interface BatchCategorySelectorProps {
open: boolean
onOpenChange: (open: boolean) => void
selectedCount: number
onSelectCategory: (category: TransactionCategory, vatTreatment?: VatTreatment) => void
progress: { done: number; total: number } | null
}
export default function BatchCategorySelector({
open,
onOpenChange,
selectedCount,
onSelectCategory,
progress,
}: BatchCategorySelectorProps) {
const [vatTreatment, setVatTreatment] = useState<VatTreatment | 'none'>('standard_25')
const isProcessing = progress !== null
const handleSelectCategory = (category: TransactionCategory) => {
const resolvedVat = vatTreatment === 'none' ? undefined : vatTreatment
onSelectCategory(category, resolvedVat)
}
return (
<Dialog open={open} onOpenChange={isProcessing ? undefined : onOpenChange}>
<DialogContent className="max-w-md">
<DialogHeader>
<DialogTitle>
{isProcessing
? `Bokför ${progress.done}/${progress.total}...`
: `Bokför ${selectedCount} transaktioner`}
</DialogTitle>
<DialogDescription>
{isProcessing
? 'Vänta medan transaktionerna bokförs'
: '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">Momsbehandling</h4>
<VatTreatmentSelect
value={vatTreatment}
onValueChange={setVatTreatment}
/>
</div>
<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={() => handleSelectCategory(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={() => handleSelectCategory(cat.value)}
>
{cat.label}
</Button>
))}
</div>
</div>
</div>
)}
</DialogContent>
</Dialog>
)
}