← back to Handbag Auth Nextjs
src/components/PriceAlertPanel.tsx
192 lines
'use client'
import { useState, useEffect } from 'react'
interface PriceAlertPanelProps {
listingId: number
externalId: string
currentPrice: number
brand?: string
model?: string
}
export function PriceAlertPanel({ listingId, externalId, currentPrice, brand, model }: PriceAlertPanelProps) {
const [alerts, setAlerts] = useState<any[]>([])
const [showForm, setShowForm] = useState(false)
const [targetPrice, setTargetPrice] = useState('')
const [alertType, setAlertType] = useState<'below' | 'above' | 'change'>('below')
const [userEmail, setUserEmail] = useState('')
const [creating, setCreating] = useState(false)
useEffect(() => {
fetchAlerts()
}, [listingId, externalId])
const fetchAlerts = async () => {
try {
const res = await fetch(`/api/price-alerts?listingId=${listingId}&externalId=${externalId}`)
const data = await res.json()
if (data.success) {
setAlerts(data.alerts.filter((a: any) => a.isActive))
}
} catch (error) {
console.error('Failed to fetch alerts:', error)
}
}
const createAlert = async () => {
if (!targetPrice) return
setCreating(true)
try {
const res = await fetch('/api/price-alerts', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
listingId,
externalId,
userEmail: userEmail || null,
targetPrice: parseFloat(targetPrice),
alertType,
}),
})
const data = await res.json()
if (data.success) {
setAlerts([...alerts, data.alert])
setTargetPrice('')
setUserEmail('')
setShowForm(false)
}
} catch (error) {
console.error('Failed to create alert:', error)
} finally {
setCreating(false)
}
}
const deleteAlert = async (alertId: number) => {
try {
const res = await fetch(`/api/price-alerts?id=${alertId}`, {
method: 'DELETE',
})
const data = await res.json()
if (data.success) {
setAlerts(alerts.filter(a => a.id !== alertId))
}
} catch (error) {
console.error('Failed to delete alert:', error)
}
}
return (
<div className="bg-white rounded-xl shadow-lg p-6 mb-8">
<div className="flex items-center justify-between mb-4">
<div>
<h3 className="text-xl font-bold text-gray-900">Price Alerts</h3>
<p className="text-sm text-gray-600">Get notified when prices change</p>
</div>
<button
onClick={() => setShowForm(!showForm)}
className="px-4 py-2 bg-gradient-to-r from-purple-600 to-blue-500 text-white rounded-lg font-medium hover:shadow-lg transition-all text-sm"
>
{showForm ? 'Cancel' : '+ New Alert'}
</button>
</div>
{/* Create Alert Form */}
{showForm && (
<div className="bg-gradient-to-br from-purple-50 to-blue-50 rounded-lg p-4 mb-4">
<div className="grid md:grid-cols-3 gap-3">
<div>
<label className="text-xs font-medium text-gray-700 mb-1 block">Alert When Price</label>
<select
value={alertType}
onChange={(e) => setAlertType(e.target.value as any)}
className="w-full px-3 py-2 bg-white border border-gray-300 rounded-lg text-sm focus:outline-none focus:ring-2 focus:ring-purple-500"
>
<option value="below">Drops Below</option>
<option value="above">Rises Above</option>
<option value="change">Changes By 10%</option>
</select>
</div>
<div>
<label className="text-xs font-medium text-gray-700 mb-1 block">Target Price (USD)</label>
<input
type="number"
placeholder={`e.g. ${Math.round(currentPrice * 0.9)}`}
value={targetPrice}
onChange={(e) => setTargetPrice(e.target.value)}
className="w-full px-3 py-2 bg-white border border-gray-300 rounded-lg text-sm focus:outline-none focus:ring-2 focus:ring-purple-500"
style={{ fontSize: '16px' }}
/>
</div>
<div>
<label className="text-xs font-medium text-gray-700 mb-1 block">Email (Optional)</label>
<input
type="email"
placeholder="you@example.com"
value={userEmail}
onChange={(e) => setUserEmail(e.target.value)}
className="w-full px-3 py-2 bg-white border border-gray-300 rounded-lg text-sm focus:outline-none focus:ring-2 focus:ring-purple-500"
style={{ fontSize: '16px' }}
/>
</div>
</div>
<button
onClick={createAlert}
disabled={creating || !targetPrice}
className="mt-3 px-4 py-2 bg-purple-600 text-white rounded-lg font-medium hover:bg-purple-700 transition-colors text-sm disabled:opacity-50"
>
{creating ? 'Creating...' : 'Create Alert'}
</button>
</div>
)}
{/* Active Alerts */}
{alerts.length > 0 ? (
<div className="space-y-2">
{alerts.map((alert) => (
<div
key={alert.id}
className="flex items-center justify-between p-3 bg-gray-50 rounded-lg border border-gray-200"
>
<div className="flex items-center space-x-3">
<div className={`w-2 h-2 rounded-full ${alert.notified ? 'bg-green-500' : 'bg-yellow-500'}`} />
<div>
<p className="text-sm font-medium text-gray-900">
{alert.alertType === 'below' && `Alert when price drops below $${alert.targetPrice.toLocaleString()}`}
{alert.alertType === 'above' && `Alert when price rises above $${alert.targetPrice.toLocaleString()}`}
{alert.alertType === 'change' && `Alert when price changes by 10%`}
</p>
{alert.userEmail && (
<p className="text-xs text-gray-500">Email: {alert.userEmail}</p>
)}
</div>
</div>
<button
onClick={() => deleteAlert(alert.id)}
className="text-red-600 hover:text-red-700 text-sm font-medium"
>
Delete
</button>
</div>
))}
</div>
) : (
<div className="text-center py-8 text-gray-500">
<svg className="mx-auto h-12 w-12 text-gray-400 mb-2" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M15 17h5l-1.405-1.405A2.032 2.032 0 0118 14.158V11a6.002 6.002 0 00-4-5.659V5a2 2 0 10-4 0v.341C7.67 6.165 6 8.388 6 11v3.159c0 .538-.214 1.055-.595 1.436L4 17h5m6 0v1a3 3 0 11-6 0v-1m6 0H9" />
</svg>
<p className="text-sm">No active alerts yet</p>
<p className="text-xs text-gray-400 mt-1">Create an alert to track price changes</p>
</div>
)}
</div>
)
}