'use client'; import { useState, useEffect } from 'react'; import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query'; import { format } from 'date-fns'; import { Loader2, Receipt, Edit, Archive } from 'lucide-react'; import { Button } from '@/components/ui/button'; import { Badge } from '@/components/ui/badge'; import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'; import { ArchiveConfirmDialog } from '@/components/shared/archive-confirm-dialog'; import { apiFetch } from '@/lib/api/client'; import { useMobileChrome } from '@/components/layout/mobile/mobile-layout-provider'; import type { ExpenseRow } from './expense-columns'; import { ExpenseDuplicateBanner } from './expense-duplicate-banner'; const PAYMENT_STATUS_COLORS: Record = { unpaid: 'bg-red-100 text-red-700 border-red-200', paid: 'bg-green-100 text-green-700 border-green-200', partial: 'bg-yellow-100 text-yellow-700 border-yellow-200', }; interface ExpenseDetailProps { expenseId: string; onEdit?: () => void; onArchived?: () => void; } export function ExpenseDetail({ expenseId, onEdit, onArchived }: ExpenseDetailProps) { const queryClient = useQueryClient(); const [archiveOpen, setArchiveOpen] = useState(false); const { data, isLoading, error } = useQuery<{ data: ExpenseRow }>({ queryKey: ['expenses', expenseId], queryFn: () => apiFetch(`/api/v1/expenses/${expenseId}`), }); const { setChrome } = useMobileChrome(); const titleForChrome: string | null = data?.data?.establishmentName ?? data?.data?.description?.slice(0, 40) ?? null; useEffect(() => { setChrome({ title: titleForChrome ?? 'Expense', showBackButton: true }); return () => setChrome({ title: null, showBackButton: false }); }, [titleForChrome, setChrome]); const archiveMutation = useMutation({ mutationFn: () => apiFetch(`/api/v1/expenses/${expenseId}`, { method: 'DELETE' }), onSuccess: () => { queryClient.invalidateQueries({ queryKey: ['expenses'] }); setArchiveOpen(false); onArchived?.(); }, }); if (isLoading) { return (
); } if (error || !data?.data) { return (
Failed to load expense details.
); } const expense = data.data; const status = expense.paymentStatus ?? 'unpaid'; const statusColor = PAYMENT_STATUS_COLORS[status] ?? ''; return (

{expense.establishmentName ?? 'Unnamed Expense'}

{format(new Date(expense.expenseDate), 'MMMM d, yyyy')}

{onEdit && ( )}
Amount

{Number(expense.amount).toLocaleString('en-US', { minimumFractionDigits: 2, maximumFractionDigits: 2, })}{' '} {expense.currency}

{expense.amountUsd && expense.currency !== 'USD' && (

≈ $ {Number(expense.amountUsd).toLocaleString('en-US', { minimumFractionDigits: 2, maximumFractionDigits: 2, })}{' '} USD

)}
Payment Status {status}
Details
Category

{expense.category?.replace(/_/g, ' ') ?? '-'}

Payment Method

{expense.paymentMethod?.replace(/_/g, ' ') ?? '-'}

Payer

{expense.payer ?? '-'}

Description

{expense.description ?? '-'}

{expense.receiptFileIds && expense.receiptFileIds.length > 0 && ( Receipts ({expense.receiptFileIds.length})
{(expense.receiptFileIds as string[]).map((fileId: string) => ( {fileId} ))}
)} archiveMutation.mutate()} isLoading={archiveMutation.isPending} />
); }