← back to Handbag Auth Nextjs
src/app/price-tracker/page.tsx
204 lines
'use client'
import { useState } from 'react'
import Link from 'next/link'
import { PriceHistoryChart } from '@/components/PriceHistoryChart'
import { PriceAlertPanel } from '@/components/PriceAlertPanel'
export default function PriceTrackerPage() {
const [searchQuery, setSearchQuery] = useState('')
const [selectedItem, setSelectedItem] = useState<any>(null)
const [searchResults, setSearchResults] = useState<any[]>([])
const [searching, setSearching] = useState(false)
const handleSearch = async () => {
if (!searchQuery) return
setSearching(true)
try {
const res = await fetch(`/api/listings?search=${encodeURIComponent(searchQuery)}&limit=10`)
const data = await res.json()
if (data.success) {
setSearchResults(data.listings)
}
} catch (error) {
console.error('Search failed:', error)
} finally {
setSearching(false)
}
}
return (
<div className="min-h-screen bg-gray-50">
{/* Header */}
<header className="bg-white shadow-sm sticky top-0 z-50">
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
<div className="flex justify-between items-center py-4">
<div className="flex items-center space-x-8">
<Link href="/" className="flex items-center space-x-2">
<div className="w-10 h-10 bg-gradient-to-br from-purple-600 to-blue-500 rounded-lg flex items-center justify-center">
<span className="text-white font-bold text-xl">HB</span>
</div>
<span className="text-xl font-bold text-gray-900">Handbag Auth</span>
</Link>
<nav className="hidden md:flex space-x-6">
<Link href="/" className="text-gray-700 hover:text-gray-900 font-medium">
Deals
</Link>
<Link href="/camera" className="text-gray-700 hover:text-gray-900 font-medium">
Camera
</Link>
<Link href="/price-tracker" className="text-purple-600 font-medium">
Price Tracker
</Link>
</nav>
</div>
<Link
href="/camera"
className="bg-gradient-to-r from-purple-600 to-blue-500 text-white px-6 py-2 rounded-lg font-medium hover:shadow-lg transition-all"
>
Scan Handbag
</Link>
</div>
</div>
</header>
{/* Main Content */}
<main className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-8">
{/* Hero Section */}
<div className="text-center mb-12">
<h1 className="text-4xl md:text-5xl font-bold text-gray-900 mb-4">
Price Tracker
</h1>
<p className="text-lg text-gray-600 max-w-2xl mx-auto">
Track historical prices of luxury handbags across Japanese marketplaces.
View price trends, identify deals, and make informed purchasing decisions.
</p>
</div>
{/* Search Section */}
<div className="bg-white rounded-xl shadow-lg p-6 mb-8">
<div className="flex gap-4">
<input
type="text"
placeholder="Search by brand, model, or item ID..."
className="flex-1 px-4 py-3 bg-gray-50 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-purple-500 focus:border-transparent text-base"
style={{ fontSize: '16px' }}
value={searchQuery}
onChange={(e) => setSearchQuery(e.target.value)}
onKeyDown={(e) => e.key === 'Enter' && handleSearch()}
/>
<button
onClick={handleSearch}
disabled={searching}
className="px-8 py-3 bg-gradient-to-r from-purple-600 to-blue-500 text-white rounded-lg font-medium hover:shadow-lg transition-all disabled:opacity-50"
>
{searching ? 'Searching...' : 'Search'}
</button>
</div>
{/* Search Results */}
{searchResults.length > 0 && (
<div className="mt-6 space-y-2">
<p className="text-sm text-gray-600 mb-3">Select an item to view price history:</p>
{searchResults.map((item) => (
<button
key={item.id}
onClick={() => setSelectedItem(item)}
className={`w-full text-left p-4 rounded-lg border-2 transition-all ${
selectedItem?.id === item.id
? 'border-purple-600 bg-purple-50'
: 'border-gray-200 hover:border-purple-300 bg-white'
}`}
>
<div className="flex items-center justify-between">
<div>
<p className="font-semibold text-gray-900">{item.brand} {item.model}</p>
<p className="text-sm text-gray-600 line-clamp-1">{item.title}</p>
</div>
<div className="text-right">
<p className="text-lg font-bold text-gray-900">${item.priceUsd?.toLocaleString()}</p>
<p className="text-sm text-gray-500">{item.source}</p>
</div>
</div>
</button>
))}
</div>
)}
</div>
{/* Price History Chart */}
{selectedItem ? (
<>
<PriceAlertPanel
listingId={selectedItem.id}
externalId={selectedItem.externalId}
currentPrice={selectedItem.priceUsd}
brand={selectedItem.brand}
model={selectedItem.model}
/>
<PriceHistoryChart
externalId={selectedItem.externalId}
listingId={selectedItem.id}
brand={selectedItem.brand}
model={selectedItem.model}
/>
</>
) : (
<div className="bg-white rounded-xl shadow-lg p-12 text-center">
<svg className="mx-auto h-16 w-16 text-gray-400 mb-4" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9 19v-6a2 2 0 00-2-2H5a2 2 0 00-2 2v6a2 2 0 002 2h2a2 2 0 002-2zm0 0V9a2 2 0 012-2h2a2 2 0 012 2v10m-6 0a2 2 0 002 2h2a2 2 0 002-2m0 0V5a2 2 0 012-2h2a2 2 0 012 2v14a2 2 0 01-2 2h-2a2 2 0 01-2-2z" />
</svg>
<h3 className="text-xl font-semibold text-gray-900 mb-2">Search for an item to get started</h3>
<p className="text-gray-600">
Enter a brand name, model, or specific item to track its price history
</p>
</div>
)}
{/* Info Section */}
<div className="mt-12 grid md:grid-cols-3 gap-6">
<div className="bg-gradient-to-br from-purple-50 to-purple-100 rounded-xl p-6">
<div className="w-12 h-12 bg-purple-600 rounded-lg flex items-center justify-center mb-4">
<svg className="w-6 h-6 text-white" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M13 7h8m0 0v8m0-8l-8 8-4-4-6 6" />
</svg>
</div>
<h3 className="text-lg font-bold text-gray-900 mb-2">Real-Time Tracking</h3>
<p className="text-sm text-gray-600">
Prices are tracked daily from multiple Japanese marketplaces to give you accurate trend data.
</p>
</div>
<div className="bg-gradient-to-br from-blue-50 to-blue-100 rounded-xl p-6">
<div className="w-12 h-12 bg-blue-600 rounded-lg flex items-center justify-center mb-4">
<svg className="w-6 h-6 text-white" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M12 8c-1.657 0-3 .895-3 2s1.343 2 3 2 3 .895 3 2-1.343 2-3 2m0-8c1.11 0 2.08.402 2.599 1M12 8V7m0 1v8m0 0v1m0-1c-1.11 0-2.08-.402-2.599-1M21 12a9 9 0 11-18 0 9 9 0 0118 0z" />
</svg>
</div>
<h3 className="text-lg font-bold text-gray-900 mb-2">Price Insights</h3>
<p className="text-sm text-gray-600">
View lowest, highest, and average prices to identify the best deals and purchasing opportunities.
</p>
</div>
<div className="bg-gradient-to-br from-green-50 to-green-100 rounded-xl p-6">
<div className="w-12 h-12 bg-green-600 rounded-lg flex items-center justify-center mb-4">
<svg className="w-6 h-6 text-white" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z" />
</svg>
</div>
<h3 className="text-lg font-bold text-gray-900 mb-2">Historical Data</h3>
<p className="text-sm text-gray-600">
Access complete price history to understand seasonal trends and make data-driven decisions.
</p>
</div>
</div>
</main>
</div>
)
}