import React, { useState, useRef } from 'react' import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query' import { Upload, Package, Search, Trash2, RefreshCw, X, ChevronLeft, ChevronRight } from 'lucide-react' import { collectionApi } from '@/api' import { Button, Input, Panel, Badge, Spinner, EmptyState, Skeleton, ErrorMessage } from '@/components/ui' import type { CollectionCard, CollectionStats } from '@/types' import { AxiosError } from 'axios' // ─── Import panel ───────────────────────────────────────────────────────────── function ImportPanel({ onSuccess }: { onSuccess: () => void }) { const [source, setSource] = useState<'archidekt' | 'manabox'>('archidekt') const [replace, setReplace] = useState(false) const [file, setFile] = useState(null) const [error, setError] = useState('') const [success, setSuccess] = useState('') const fileRef = useRef(null) const importMutation = useMutation({ mutationFn: () => collectionApi.import(source, file!, replace), onSuccess: (res) => { setSuccess(`Imported ${res.data.imported ?? 'cards'} successfully.`) setFile(null) setError('') onSuccess() }, onError: (err) => { if (err instanceof AxiosError) { setError(err.response?.data?.detail ?? 'Import failed.') } else { setError('Import failed.') } }, }) return (

Import collection

{/* Source selector */}
{(['archidekt', 'manabox'] as const).map((s) => ( ))}
{/* File drop zone */}
fileRef.current?.click()} className={` border-2 border-dashed rounded-lg p-6 text-center cursor-pointer transition-all ${file ? 'border-accent-violet/50 bg-accent-violet/5' : 'border-bg-border hover:border-accent-violet/40 hover:bg-bg-elevated'} `} > setFile(e.target.files?.[0] ?? null)} /> {file ? (
{file.name}
) : (

Click to choose {source === 'archidekt' ? 'CSV or JSON' : 'CSV'} file

)}
{error && } {success && (
{success}
)}
) } // ─── Stats panel ────────────────────────────────────────────────────────────── function StatsPanel() { const { data } = useQuery({ queryKey: ['collection-stats'], queryFn: async () => (await collectionApi.stats()).data, }) if (!data) return null return (
{[ { label: 'Total cards', value: data.total_cards.toLocaleString() }, { label: 'Unique cards', value: data.unique_cards.toLocaleString() }, { label: 'Foil', value: data.foil_cards.toLocaleString() }, { label: 'Est. value', value: data.estimated_value != null ? `$${data.estimated_value.toFixed(2)}` : '—', }, ].map((s) => (

{s.value}

{s.label}

))}
) } // ─── Card row ───────────────────────────────────────────────────────────────── function CollectionCardRow({ card, onDelete, }: { card: CollectionCard onDelete: (id: number) => void }) { const img = card.scryfall_data?.image_uris?.small const price = card.scryfall_data?.prices?.usd return (
{img ? ( {card.card_name} ) : (
)}

{card.card_name}

{card.set_code && ( {card.set_code} )} {card.collector_number && ( #{card.collector_number} )}
{card.quantity > 0 && ( {card.quantity}× )} {card.foil_quantity > 0 && ( {card.foil_quantity}✦ )}
{price && ${price}}
) } // ─── Main page ──────────────────────────────────────────────────────────────── export function CollectionPage() { const [search, setSearch] = useState('') const [page, setPage] = useState(1) const queryClient = useQueryClient() const { data, isLoading } = useQuery({ queryKey: ['collection', search, page], queryFn: async () => (await collectionApi.list(search, page)).data, }) const deleteMutation = useMutation({ mutationFn: (id: number) => collectionApi.deleteCard(id), onSuccess: () => { queryClient.invalidateQueries({ queryKey: ['collection'] }) queryClient.invalidateQueries({ queryKey: ['collection-stats'] }) }, }) const clearMutation = useMutation({ mutationFn: () => collectionApi.clear(), onSuccess: () => { queryClient.invalidateQueries({ queryKey: ['collection'] }) queryClient.invalidateQueries({ queryKey: ['collection-stats'] }) }, }) const invalidate = () => { queryClient.invalidateQueries({ queryKey: ['collection'] }) queryClient.invalidateQueries({ queryKey: ['collection-stats'] }) } const cards: CollectionCard[] = data?.items ?? [] const total: number = data?.total ?? 0 const pages: number = data?.pages ?? 1 return (

Collection

{total > 0 && ( )}
{/* Search */}
{ setSearch(e.target.value); setPage(1) }} placeholder="Search collection…" className="w-full bg-bg-elevated border border-bg-border rounded-md pl-9 pr-3 py-2 text-sm text-text-primary placeholder:text-text-muted focus:outline-none focus:ring-2 focus:ring-accent-violet focus:border-transparent" />
{/* Card list */} {isLoading ? (
{[1, 2, 3, 4, 5].map((n) => (
))}
) : cards.length === 0 ? ( } title={search ? 'No matching cards' : 'Collection is empty'} description={search ? 'Try a different search.' : 'Import your collection above.'} /> ) : (
{cards.map((card) => ( deleteMutation.mutate(id)} /> ))}
)}
{/* Pagination */} {pages > 1 && (
{total} cards
Page {page} of {pages}
)}
) }