'use client'; import { useRef, useState, useEffect } from 'react'; import { useRouter } from 'next/navigation'; import { Camera, Loader2, RotateCcw, AlertTriangle, CheckCircle2, Save } from 'lucide-react'; import { Button } from '@/components/ui/button'; import { Input } from '@/components/ui/input'; import { Label } from '@/components/ui/label'; import { Textarea } from '@/components/ui/textarea'; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from '@/components/ui/select'; import { useUIStore } from '@/stores/ui-store'; import { apiFetch } from '@/lib/api/client'; import { cn } from '@/lib/utils'; import { EXPENSE_CATEGORIES, PAYMENT_METHODS } from '@/lib/constants'; // ─── Types ──────────────────────────────────────────────────────────────────── interface ParsedReceipt { establishment: string | null; date: string | null; amount: number | null; currency: string | null; lineItems: Array<{ description: string; amount: number }>; confidence: number; } type ScanState = | { kind: 'idle' } | { kind: 'processing' } | { kind: 'verify'; parsed: ParsedReceipt; source: 'ai' | 'manual'; reason?: string; providerError?: string; } | { kind: 'saving' } | { kind: 'saved'; expenseId: string } | { kind: 'error'; message: string }; interface ScanResp { data: { parsed: ParsedReceipt; source: 'ai' | 'manual'; reason?: string; provider?: string; model?: string; providerError?: string; }; } // ─── Form ───────────────────────────────────────────────────────────────────── interface VerifyFormProps { parsed: ParsedReceipt; imagePreview: string; imageFile: File; source: 'ai' | 'manual'; reason?: string; providerError?: string; onSubmit: (input: { establishmentName: string; amount: string; currency: string; expenseDate: string; category: string; paymentMethod: string; description: string; file: File; }) => void; onRetake: () => void; saving: boolean; } const TODAY = () => new Date().toISOString().slice(0, 10); function VerifyForm({ parsed, imagePreview, imageFile, source, reason, providerError, onSubmit, onRetake, saving, }: VerifyFormProps) { const [establishmentName, setEstablishment] = useState(parsed.establishment ?? ''); const [amount, setAmount] = useState(parsed.amount != null ? String(parsed.amount) : ''); const [currency, setCurrency] = useState((parsed.currency ?? 'USD').toUpperCase()); const [expenseDate, setExpenseDate] = useState(parsed.date ?? TODAY()); const [category, setCategory] = useState('other'); const [paymentMethod, setPaymentMethod] = useState('credit_card'); const [description, setDescription] = useState(''); const lowConfidence = source === 'ai' && parsed.confidence < 0.6; const noOcr = source === 'manual'; const banner = noOcr ? (
{reason === 'no-ocr-configured' ? ( <>

Manual entry mode

No AI provider is configured for this port. Fill in the details below to save the expense with the photo attached.

) : ( <>

We couldn't read the receipt automatically

{providerError ? `Reason: ${providerError}.` : ''} Fill in the details below to save the expense with the photo attached.

)}
) : lowConfidence ? (

Low-confidence read — please double-check the fields

The AI returned a confidence of {Math.round(parsed.confidence * 100)}%.

) : (

Receipt parsed — confirm the fields and save

Confidence {Math.round(parsed.confidence * 100)}%.

); return (
{ e.preventDefault(); onSubmit({ establishmentName, amount, currency, expenseDate, category, paymentMethod, description, file: imageFile, }); }} > {banner}
{/* eslint-disable-next-line @next/next/no-img-element */} Receipt preview
setEstablishment(e.target.value)} placeholder="e.g. Marina Fuel Station" />
setAmount(e.target.value)} required />
setCurrency(e.target.value.toUpperCase())} maxLength={3} required />
setExpenseDate(e.target.value)} required />