'use client' import { useState, useMemo } from 'react' import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card' import { Button } from '@/components/ui/button' import { Input } from '@/components/ui/input' import { Badge } from '@/components/ui/badge' import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from '@/components/ui/select' import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow, } from '@/components/ui/table' import { ArrowRight, Search, CheckCircle, AlertCircle, XCircle, Filter, } from 'lucide-react' import type { AccountMapping } from '@/lib/import/types' import type { BASAccount } from '@/types' import { getAccountClassName } from '@/lib/bookkeeping/account-descriptions' interface AccountMappingStepProps { mappings: AccountMapping[] basAccounts: BASAccount[] onMappingChange: (sourceAccount: string, targetAccount: string, targetName: string) => void onContinue: () => void onBack: () => void } type FilterType = 'all' | 'unmapped' | 'low_confidence' | 'manual' const PAGE_SIZE = 50 export default function AccountMappingStep({ mappings, basAccounts, onMappingChange, onContinue, onBack, }: AccountMappingStepProps) { const [searchTerm, setSearchTerm] = useState('') // Default to showing unmapped accounts first (most actionable) const [filter, setFilter] = useState(() => { const hasUnmapped = mappings.some((m) => !m.targetAccount) return hasUnmapped ? 'unmapped' : 'all' }) const [currentPage, setCurrentPage] = useState(1) // Filter and search mappings const filteredMappings = useMemo(() => { let result = mappings // Apply filter switch (filter) { case 'unmapped': result = result.filter((m) => !m.targetAccount) break case 'low_confidence': result = result.filter((m) => m.targetAccount && m.confidence < 0.7) break case 'manual': result = result.filter((m) => m.isOverride) break } // Apply search if (searchTerm) { const term = searchTerm.toLowerCase() result = result.filter( (m) => m.sourceAccount.includes(term) || m.sourceName.toLowerCase().includes(term) || m.targetAccount?.includes(term) || m.targetName?.toLowerCase().includes(term) ) } return result }, [mappings, filter, searchTerm]) // Pagination const totalPages = Math.ceil(filteredMappings.length / PAGE_SIZE) const paginatedMappings = useMemo(() => { const start = (currentPage - 1) * PAGE_SIZE return filteredMappings.slice(start, start + PAGE_SIZE) }, [filteredMappings, currentPage]) // Reset page when filter or search changes const handleFilterChange = (newFilter: FilterType) => { setFilter(newFilter) setCurrentPage(1) } const handleSearchChange = (term: string) => { setSearchTerm(term) setCurrentPage(1) } // Calculate stats const stats = useMemo(() => { const unmapped = mappings.filter((m) => !m.targetAccount).length const lowConfidence = mappings.filter((m) => m.targetAccount && m.confidence < 0.7).length const manual = mappings.filter((m) => m.isOverride).length return { unmapped, lowConfidence, manual } }, [mappings]) const canContinue = stats.unmapped === 0 // Group BAS accounts by class for the dropdown const accountsByClass = useMemo(() => { const groups: { [key: string]: BASAccount[] } = {} for (const account of basAccounts) { const className = getAccountClassName(account.account_class) if (!groups[className]) { groups[className] = [] } groups[className].push(account) } return groups }, [basAccounts]) return (
Kontomappning Granska och justera mappningarna mellan importerade konton och din kontoplan. Alla konton måste vara mappade innan importen kan genomföras. {/* Stats */}
0 ? 'destructive' : 'secondary'} className="cursor-pointer" onClick={() => handleFilterChange('unmapped')} > {stats.unmapped} ej mappade 0 ? 'secondary' : 'outline'} className="cursor-pointer" onClick={() => handleFilterChange('low_confidence')} > {stats.lowConfidence} osäkra handleFilterChange('manual')} > {stats.manual} manuellt satta handleFilterChange('all')} > Visa alla ({mappings.length})
{/* Search and filter */}
handleSearchChange(e.target.value)} className="pl-9" />
{/* Mapping table */}
Källkonto Källnamn Målkonto Konfidens {paginatedMappings.map((mapping) => ( {mapping.sourceAccount} {mapping.sourceName} {mapping.targetAccount && ( )} ))} {paginatedMappings.length === 0 && ( Inga konton matchar filtret )}
{/* Pagination */} {totalPages > 1 && (

Visar {((currentPage - 1) * PAGE_SIZE) + 1}-{Math.min(currentPage * PAGE_SIZE, filteredMappings.length)} av {filteredMappings.length}

Sida {currentPage} av {totalPages}
)}
{/* Actions */}
) } function ConfidenceBadge({ confidence, isOverride, }: { confidence: number matchType: string // Keep for potential future use isOverride: boolean }) { if (isOverride) { return Manuell } if (confidence >= 0.9) { return Exakt } if (confidence >= 0.7) { return Trolig } return Osäker }