PriceNova
import { useState, useEffect, useRef } from "react";
// ─── Mock Data & Helpers ───────────────────────────────────────────────────────
const SAMPLE_PRODUCTS = [
{
asin: "B08N5WRWNW",
title: "Apple AirPods Pro (2nd Generation)",
brand: "Apple",
category: "Electronics",
image: "🎧",
currentPrice: 189.99,
originalPrice: 249.00,
rating: 4.7,
reviews: 84312,
bsr: 3,
},
{
asin: "B0BDHX8Z63",
title: "Sony WH-1000XM5 Wireless Headphones",
brand: "Sony",
category: "Electronics",
image: "🎵",
currentPrice: 278.00,
originalPrice: 399.99,
rating: 4.6,
reviews: 41200,
bsr: 7,
},
{
asin: "B09G9FPHY6",
title: "Kindle Paperwhite (16 GB)",
brand: "Amazon",
category: "Electronics",
image: "📖",
currentPrice: 99.99,
originalPrice: 139.99,
rating: 4.8,
reviews: 128900,
bsr: 2,
},
{
asin: "B0CMDWC436",
title: "Instant Pot Duo 7-in-1 Electric Pressure Cooker",
brand: "Instant Pot",
category: "Kitchen",
image: "🍲",
currentPrice: 59.99,
originalPrice: 99.95,
rating: 4.7,
reviews: 215400,
bsr: 1,
},
];
function generatePriceHistory(basePrice, days = 365) {
const history = [];
let price = basePrice * 1.15;
const now = Date.now();
for (let i = days; i >= 0; i--) {
const t = now - i * 86400000;
const spike = Math.random() < 0.05 ? (Math.random() * 0.3 - 0.15) : 0;
const drift = (Math.random() - 0.5) * 0.02;
price = Math.max(basePrice * 0.7, Math.min(basePrice * 1.4, price * (1 + drift + spike)));
if (i % 30 < 5) price = basePrice * (0.88 + Math.random() * 0.06); // sale events
history.push({ t, price: Math.round(price * 100) / 100 });
}
return history;
}
function dealScore(product) {
const discount = (product.originalPrice - product.currentPrice) / product.originalPrice;
const ratingScore = (product.rating - 3) / 2;
const reviewScore = Math.min(product.reviews / 100000, 1);
const bsrScore = 1 - Math.min(product.bsr / 20, 1);
return Math.round((discount * 40 + ratingScore * 25 + reviewScore * 20 + bsrScore * 15));
}
// ─── Mini Chart ───────────────────────────────────────────────────────────────
function PriceChart({ history, currentPrice, color = "#00ff88" }) {
const W = 480, H = 110;
if (!history || history.length === 0) return null;
const prices = history.map(h => h.price);
const minP = Math.min(...prices) * 0.98;
const maxP = Math.max(...prices) * 1.02;
const toX = (i) => (i / (history.length - 1)) * W;
const toY = (p) => H - ((p - minP) / (maxP - minP)) * H;
const points = history.map((h, i) => `${toX(i)},${toY(h.price)}`).join(" ");
const fillPoints = `0,${H} ` + points + ` ${W},${H}`;
const currentY = toY(currentPrice);
return (
);
}
// ─── Deal Score Badge ──────────────────────────────────────────────────────────
function ScoreBadge({ score }) {
const color = score >= 75 ? "#00ff88" : score >= 50 ? "#ffcc00" : "#ff6b6b";
const label = score >= 75 ? "HOT" : score >= 50 ? "GOOD" : "FAIR";
return (
{/* Price Metrics */}
{/* Extra stats */}
{/* AI Section */}
);
}
// ─── Search Bar ───────────────────────────────────────────────────────────────
function SearchBar({ query, setQuery }) {
return (
{label} {score}
);
}
// ─── AI Insight Panel ─────────────────────────────────────────────────────────
function AIInsightPanel({ product, history }) {
const [insight, setInsight] = useState("");
const [loading, setLoading] = useState(false);
const [open, setOpen] = useState(false);
async function fetchInsight() {
if (insight) { setOpen(true); return; }
setLoading(true);
setOpen(true);
const minP = Math.min(...history.map(h => h.price));
const maxP = Math.max(...history.map(h => h.price));
const avgP = history.reduce((s, h) => s + h.price, 0) / history.length;
const prompt = `You are a sharp Amazon deal analyst. Analyze this product and give a crisp, actionable 3-sentence insight. No fluff.
Product: ${product.title}
Brand: ${product.brand}
Category: ${product.category}
Current Price: $${product.currentPrice}
Original/List Price: $${product.originalPrice}
30-day Price Range: $${minP.toFixed(2)} – $${maxP.toFixed(2)}
Average Price (365d): $${avgP.toFixed(2)}
Rating: ${product.rating}/5 (${product.reviews.toLocaleString()} reviews)
Best Seller Rank: #${product.bsr} in ${product.category}
Deal Score: ${dealScore(product)}/100
Give: 1) Is this actually a good deal right now? 2) Price trend prediction. 3) Should the buyer act now or wait?`;
try {
const res = await fetch("https://api.anthropic.com/v1/messages", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
model: "claude-sonnet-4-20250514",
max_tokens: 1000,
messages: [{ role: "user", content: prompt }],
}),
});
const data = await res.json();
const text = data.content?.filter(b => b.type === "text").map(b => b.text).join("") || "No insight available.";
setInsight(text);
} catch (e) {
setInsight("Failed to load AI insight. Please try again.");
}
setLoading(false);
}
return (
{open && (
);
}
// ─── Product Card ──────────────────────────────────────────────────────────────
function ProductCard({ product, onSelect, selected }) {
const score = dealScore(product);
const discount = Math.round((1 - product.currentPrice / product.originalPrice) * 100);
const history = useRef(generatePriceHistory(product.currentPrice)).current;
return (
{loading ? (
)}
)}
⟳
Analyzing with AI...
) : (
✦ AI ANALYSIS
{insight}
onSelect({ product, history })}
style={{
background: selected ? "rgba(0,255,136,0.04)" : "rgba(255,255,255,0.03)",
border: `1.5px solid ${selected ? "rgba(0,255,136,0.4)" : "rgba(255,255,255,0.08)"}`,
borderRadius: 14,
padding: "16px",
cursor: "pointer",
transition: "all 0.2s",
}}
>
);
}
// ─── Detail Panel ──────────────────────────────────────────────────────────────
function DetailPanel({ data }) {
const { product, history } = data;
const prices = history.map(h => h.price);
const minP = Math.min(...prices);
const maxP = Math.max(...prices);
const avgP = prices.reduce((s, p) => s + p, 0) / prices.length;
const score = dealScore(product);
const discount = Math.round((1 - product.currentPrice / product.originalPrice) * 100);
const [range, setRange] = useState(90);
const filteredHistory = history.slice(-range);
return (
{product.image}
{product.title}
{product.brand} · {product.category}
${product.currentPrice}
${product.originalPrice}
-{discount}%
★ {product.rating} · {product.reviews.toLocaleString()} reviews · BSR #{product.bsr}
{product.image}
{product.title}
ASIN: {product.asin} · {product.brand}
{[
{ label: "Current", value: `$${product.currentPrice}`, color: "#00ff88" },
{ label: "All-time Low", value: `$${minP.toFixed(2)}`, color: "#60a5fa" },
{ label: "All-time High", value: `$${maxP.toFixed(2)}`, color: "#f87171" },
{ label: "Avg (1yr)", value: `$${avgP.toFixed(2)}`, color: "#fbbf24" },
].map(m => (
))}
{/* Chart */}
{m.label.toUpperCase()}
{m.value}
PRICE HISTORY
{[30, 90, 180, 365].map(d => (
))}
DEAL BREAKDOWN
Discount -{discount}%
You save ${(product.originalPrice - product.currentPrice).toFixed(2)}
vs Avg
{product.currentPrice < avgP ? "↓" : "↑"} ${Math.abs(product.currentPrice - avgP).toFixed(2)}
PRODUCT STATS
Rating ★ {product.rating}
Reviews {product.reviews.toLocaleString()}
BSR #{product.bsr}
setQuery(e.target.value)}
placeholder="Search products or paste ASIN..."
style={{
width: "100%",
background: "rgba(255,255,255,0.05)",
border: "1.5px solid rgba(255,255,255,0.1)",
borderRadius: 10,
padding: "11px 16px 11px 40px",
color: "#e8e8e8",
fontSize: 14,
outline: "none",
boxSizing: "border-box",
fontFamily: "inherit",
}}
/>
⌕
);
}
// ─── Main App ──────────────────────────────────────────────────────────────────
export default function App() {
const [query, setQuery] = useState("");
const [selected, setSelected] = useState(null);
const [tab, setTab] = useState("tracker");
const [alertList, setAlertList] = useState([
{ product: SAMPLE_PRODUCTS[0], targetPrice: 179 },
{ product: SAMPLE_PRODUCTS[2], targetPrice: 89 },
]);
const [newAlert, setNewAlert] = useState({ asin: "", price: "" });
const filtered = SAMPLE_PRODUCTS.filter(p =>
p.title.toLowerCase().includes(query.toLowerCase()) ||
p.brand.toLowerCase().includes(query.toLowerCase()) ||
p.asin.toLowerCase().includes(query.toLowerCase())
);
useEffect(() => {
const style = document.createElement("style");
style.textContent = `
@import url('https://fonts.googleapis.com/css2?family=Space+Mono:wght@400;700&family=Syne:wght@400;600;700;800&display=swap');
* { box-sizing: border-box; margin: 0; padding: 0; }
body { background: #0a0a0f; }
@keyframes spin { from { transform: rotate(0deg); } to { transform: rotate(360deg); } }
::-webkit-scrollbar { width: 4px; } ::-webkit-scrollbar-track { background: transparent; } ::-webkit-scrollbar-thumb { background: #222; border-radius: 4px; }
input::placeholder { color: #444; }
`;
document.head.appendChild(style);
return () => document.head.removeChild(style);
}, []);
return (
{/* Header */}
);
}
⬡
PriceNova
AI PRICE INTELLIGENCE
{["tracker", "alerts", "compare"].map(t => (
))}
{/* ── TRACKER TAB ── */}
{tab === "tracker" && (
)}
)}
{/* ── ALERTS TAB ── */}
{tab === "alerts" && (
{alertList.map((a, i) => {
const triggered = a.product.currentPrice <= a.targetPrice;
return (
);
})}
)}
{/* ── COMPARE TAB ── */}
{tab === "compare" && (
)}
{filtered.map(p => (
setSelected(selected?.product.asin === p.asin ? null : d)}
/>
))}
{selected && (
Price Alerts
Get notified when prices drop to your target
+ ADD ALERT
setNewAlert(a => ({ ...a, asin: e.target.value }))}
placeholder="ASIN or product name"
style={{ flex: 2, background: "rgba(255,255,255,0.05)", border: "1px solid rgba(255,255,255,0.1)", borderRadius: 8, padding: "9px 12px", color: "#e8e8e8", fontSize: 13, outline: "none", fontFamily: "inherit" }}
/>
setNewAlert(a => ({ ...a, price: e.target.value }))}
placeholder="Target $"
type="number"
style={{ flex: 1, background: "rgba(255,255,255,0.05)", border: "1px solid rgba(255,255,255,0.1)", borderRadius: 8, padding: "9px 12px", color: "#e8e8e8", fontSize: 13, outline: "none", fontFamily: "inherit" }}
/>
{a.product.image}
{a.product.title}
Target: ${a.targetPrice} · Current: ${a.product.currentPrice}
{triggered && ✓ TRIGGERED}
Compare Products
Side-by-side deal analysis
| PRODUCT | {["PRICE", "ORIGINAL", "DISCOUNT", "RATING", "REVIEWS", "BSR", "DEAL SCORE"].map(h => ({h} | ))}||||||
|---|---|---|---|---|---|---|---|
|
{p.image}
{p.title.slice(0, 36)}…
{p.brand}
|
${p.currentPrice} | ${p.originalPrice} | -{discount}% | ★ {p.rating} | {p.reviews.toLocaleString()} | #{p.bsr} |
Comments
Post a Comment