'use client' import { useMemo, useState, useCallback } from 'react' import { Card, CardContent, CardHeader, CardTitle, CardDescription } from '@/components/ui/card' import { Button } from '@/components/ui/button' import { Input } from '@/components/ui/input' import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select' import { Switch } from '@/components/ui/switch' import { Label } from '@/components/ui/label' import { Trash2, AlertTriangle, Loader2, RefreshCw } from 'lucide-react' import { cn } from '@/lib/utils' import type { ArticleType } from '@/types' import type { AnnotatedArticleRow } from '@/lib/import/articles/types' let idCounter = 0 const newId = () => `art_row_${++idCounter}_${Date.now()}` interface EditableArticleRow extends AnnotatedArticleRow { id: string } interface ArticlesEditStepProps { rows: AnnotatedArticleRow[] onExecute: (rows: AnnotatedArticleRow[], updateDuplicates: boolean) => void onBack: () => void isLoading: boolean error: string | null } const TYPE_LABELS: Record = { vara: 'Vara', tjanst: 'Tjänst', } const VAT_RATES = [25, 12, 6, 0] as const export default function ArticlesEditStep({ rows: initialRows, onExecute, onBack, isLoading, error, }: ArticlesEditStepProps) { const [rows, setRows] = useState(() => initialRows.map((r) => ({ ...r, id: newId() })), ) const [updateDuplicates, setUpdateDuplicates] = useState(false) const liveDuplicateCount = useMemo( () => rows.filter((r) => r.duplicate_match !== null).length, [rows], ) const newCount = rows.length - liveDuplicateCount const hasErrors = useMemo(() => rows.some((r) => !r.is_valid), [rows]) const adjustedVatCount = useMemo( () => rows.filter((r) => r.vat_rate_adjusted).length, [rows], ) const canContinue = rows.length > 0 && !hasErrors && !isLoading const updateRow = useCallback((id: string, updates: Partial) => { setRows((prev) => prev.map((r) => (r.id === id ? { ...r, ...updates } : r))) }, []) const deleteRow = useCallback((id: string) => { setRows((prev) => prev.filter((r) => r.id !== id)) }, []) const handlePriceChange = useCallback((id: string, raw: string) => { const n = parseFloat(raw.replace(',', '.')) const price = Number.isFinite(n) ? n : 0 updateRow(id, { price_excl_vat: price, is_valid: price >= 0, validation_errors: price < 0 ? ['Priset kan inte vara negativt'] : [], }) }, [updateRow]) const handleExecute = () => { if (!canContinue) return const stripped: AnnotatedArticleRow[] = rows.map(({ id: _id, ...rest }) => rest) onExecute(stripped, updateDuplicates) } return ( Granska artiklar Kontrollera att uppgifterna stämmer. Du kan justera benämning, typ, pris och moms inline, eller ta bort rader. {newCount} ny{newCount === 1 ? '' : 'a'} artik{newCount === 1 ? 'el' : 'lar'} skapas {liveDuplicateCount > 0 ? ` och ${liveDuplicateCount} matchar befintliga.` : '.'} {/* Duplicate handling banner */} {liveDuplicateCount > 0 && (

{liveDuplicateCount} rader matchar befintliga artiklar (på artikelnummer eller benämning).

{updateDuplicates && (

Endast fält med värden i filen skrivs över. Typ, enhet och moms lämnas orörda.

)}
)} {/* VAT-adjustment notice: rows whose rate was snapped/defaulted */} {adjustedVatCount > 0 && (

{adjustedVatCount} rad{adjustedVatCount === 1 ? '' : 'er'} fick momssatsen omtolkad (avrundad till närmaste giltiga, eller satt till 25 %). Kontrollera de markerade raderna innan du importerar: fel momssats ger fel moms på fakturan.

)} {/* Table */}
{rows.map((row) => ( ))}
Art.nr Benämning Typ Pris exkl moms Moms Status
{row.article_number || 'Auto'} updateRow(row.id, { name: e.target.value })} className="h-8" />
handlePriceChange(row.id, e.target.value)} className="h-8 text-right tabular-nums" /> {/* Exception chip: only non-SEK rows carry a marker. */} {row.currency && row.currency !== 'SEK' && ( {row.currency} )}
{row.vat_rate_adjusted && ( )}
{!row.is_valid && ( )} {row.duplicate_match ? ( {updateDuplicates ? 'Uppdateras' : 'Hoppas över'} ) : ( Ny )}
{hasErrors && (

Vissa rader har valideringsfel (markerade i rött). Åtgärda eller ta bort dem innan du fortsätter.

)} {error && (

{error}

)}
) }