'use client'; import { useState, useCallback } from 'react'; import type { SearchResult as FullSearchResult, FitmentEntry, PriceSource } from '@/lib/ebay-tools/types'; /* ── Types (페이지 전용) ──────────────────────────────────── */ // SearchResult['data']에 해당하는 타입 (API 응답의 data 필드) type PageSearchResult = FullSearchResult['data']; interface HistoryItem { partNumber: string; partName?: string; time: string; resultSummary: string; } /* ── Tab IDs ────────────────────────────────────────────────── */ const TABS = [ { id: 'basic', label: '기본 정보' }, { id: 'listing', label: '이베이 리스팅' }, { id: 'fitment', label: '호환 차종' }, { id: 'pricing', label: '가격 비교' }, { id: 'raw', label: '원본 데이터' }, ] as const; type TabId = (typeof TABS)[number]['id']; /* ── Icons (inline SVGs) ────────────────────────────────────── */ const SearchIcon = () => ( ); const SpinnerIcon = () => ( ); const CopyIcon = () => ( ); const ClockIcon = () => ( ); /* ── Component ──────────────────────────────────────────────── */ export default function EbayPartsPage() { const [partNumber, setPartNumber] = useState(''); const [partName, setPartName] = useState(''); const [loading, setLoading] = useState(false); const [error, setError] = useState(null); const [result, setResult] = useState(null); const [activeTab, setActiveTab] = useState('basic'); const [history, setHistory] = useState([]); const [copied, setCopied] = useState(false); const [rawExpanded, setRawExpanded] = useState(false); /* ── Search ─────────────────────────────────────────────── */ const handleSearch = useCallback( async (pn?: string, pnm?: string) => { const searchPartNumber = pn ?? partNumber; const searchPartName = pnm ?? partName; if (!searchPartNumber.trim()) { setError('품번을 입력해주세요.'); return; } setLoading(true); setError(null); setResult(null); setActiveTab('basic'); try { const res = await fetch('/api/tools/ebay-parts/search', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ partNumber: searchPartNumber.trim(), partName: searchPartName.trim() || undefined, }), }); const json = await res.json(); if (!res.ok || !json.success) { setError(json.error || '검색에 실패했습니다.'); return; } setResult(json.data); // Update history setHistory((prev) => { const entry: HistoryItem = { partNumber: searchPartNumber.trim(), partName: searchPartName.trim() || undefined, time: new Date().toLocaleTimeString('ko-KR'), resultSummary: `${json.data.fitment.length}개 차종, ${json.data.pricing.sources.length}개 소스`, }; return [entry, ...prev.filter((h) => h.partNumber !== entry.partNumber)].slice(0, 5); }); } catch { setError('네트워크 오류가 발생했습니다. 다시 시도해주세요.'); } finally { setLoading(false); } }, [partNumber, partName] ); const handleHistoryClick = (item: HistoryItem) => { setPartNumber(item.partNumber); setPartName(item.partName || ''); handleSearch(item.partNumber, item.partName); }; const handleCopy = async (text: string) => { await navigator.clipboard.writeText(text); setCopied(true); setTimeout(() => setCopied(false), 1500); }; /* ── Confidence Badge ──────────────────────────────────── */ const ConfidenceBadge = ({ level }: { level: string }) => { const styles: Record = { high: 'bg-emerald-50 text-emerald-700 border-emerald-200', medium: 'bg-amber-50 text-amber-700 border-amber-200', low: 'bg-red-50 text-red-700 border-red-200', }; const labels: Record = { high: 'High', medium: 'Medium', low: 'Low' }; return ( {labels[level] || level} ); }; /* ── Tab Content Renderers ─────────────────────────────── */ const renderBasicInfo = () => { if (!result) return null; const { basicInfo } = result; return (
{[ ['부품명', basicInfo.partName], ['브랜드', basicInfo.brand], ['품번', basicInfo.partNumber], ['카테고리', basicInfo.category], ].map(([label, value]) => (

{label}

{value}

))}

OEM 번호

{basicInfo.oemNumbers.map((num) => ( {num} ))}
); }; const renderListing = () => { if (!result) return null; const { listing } = result; return (
{/* Title */}

추천 제목

{listing.title}

{/* Category */}

추천 카테고리

{listing.category}

{/* Item Specifics */}

Item Specifics

{Object.entries(listing.itemSpecifics).map(([key, value]) => ( ))}
Key Value
{key} {value}
); }; const renderFitment = () => { if (!result) return null; return (

호환 차종 목록

{result.fitment.map((f, i) => ( ))}
Year Make Model Engine 신뢰도
{f.year} {f.make} {f.model} {f.engine}
); }; const renderPricing = () => { if (!result) return null; const { pricing } = result; return (
{/* Price table */}

소스별 가격 비교

{pricing.sources.map((s) => ( ))}
사이트 가격 (USD) 원화 환산 링크
{s.site} ${s.price.toFixed(2)} {Math.round(s.price * pricing.exchangeRate.rate).toLocaleString()}원 바로가기
{/* Exchange + customs */}

환율 정보

1 USD = {pricing.exchangeRate.rate.toLocaleString()}원

{pricing.exchangeRate.source} ({pricing.exchangeRate.date})

관세/부가세 참고

{pricing.customs.isExempt && (

$150 이하 소액면세 대상

)}

HS Code: {pricing.customs.hsCode}

세율: {pricing.customs.dutyRate}

예상 관세: {pricing.customs.estimatedDuty.toLocaleString()}원

부가세 (VAT 10%): {pricing.customs.vat.toLocaleString()}원

총 수입 비용: {pricing.customs.totalImportCost.toLocaleString()}원

{pricing.customs.disclaimer}

); }; const renderRawData = () => { if (!result) return null; return (
{rawExpanded && (
              {JSON.stringify(result.rawData, null, 2)}
            
)}
); }; const renderTabContent = () => { switch (activeTab) { case 'basic': return renderBasicInfo(); case 'listing': return renderListing(); case 'fitment': return renderFitment(); case 'pricing': return renderPricing(); case 'raw': return renderRawData(); } }; /* ── Skeleton ──────────────────────────────────────────── */ const Skeleton = () => (

AI가 사이트를 탐색하고 있습니다...

{[1, 2, 3].map((i) => (
))}
); /* ── Render ────────────────────────────────────────────── */ return (
{/* ── Header ──────────────────────────────────────── */}

이베이 자동차 부품 AI 리스팅 자동화

MVP 데모

품번을 입력하면 AI가 자동으로 리스팅 정보를 수집합니다

{/* ── Input Form ──────────────────────────────────── */}
setPartNumber(e.target.value)} placeholder="예: 16610-0H040" className="w-full px-3 py-2.5 text-sm border border-slate-200 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500/30 focus:border-blue-500 transition text-slate-900 placeholder:text-slate-400" onKeyDown={(e) => e.key === 'Enter' && handleSearch()} />
setPartName(e.target.value)} placeholder="예: Fuel Pump Assembly" className="w-full px-3 py-2.5 text-sm border border-slate-200 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500/30 focus:border-blue-500 transition text-slate-900 placeholder:text-slate-400" onKeyDown={(e) => e.key === 'Enter' && handleSearch()} />
{/* ── Error ───────────────────────────────────────── */} {error && (

{error}

)} {/* ── Loading ─────────────────────────────────────── */} {loading && } {/* ── Result Tabs ─────────────────────────────────── */} {result && !loading && (
{/* Tabs */}
{TABS.map((tab) => ( ))}
{/* Tab Content */}
{renderTabContent()}
{/* Meta footer */}
검색 시각: {new Date(result.meta.searchedAt).toLocaleString('ko-KR')} 소요 시간: {result.meta.processingTime} 소스: {result.meta.sourcesChecked.join(', ')} 모델: {result.meta.aiModel}
)} {/* ── Search History ──────────────────────────────── */} {history.length > 0 && (

최근 검색

{history.map((item, i) => ( ))}
)}
); }