← back to Watches
src/components/affiliate/OmegaPriceChart.jsx
180 lines
/**
* OMEGA Price Chart - TradingView Style
* Real-time price tracking with historical data
*/
import React, { useEffect, useRef } from 'react';
import { createChart } from 'lightweight-charts';
export default function OmegaPriceChart({ data, title = 'OMEGA Price History', model = '' }) {
const chartContainerRef = useRef();
const chartRef = useRef(null);
useEffect(() => {
if (!chartContainerRef.current) return;
// Create chart instance
const chart = createChart(chartContainerRef.current, {
width: chartContainerRef.current.clientWidth,
height: 400,
layout: {
background: { color: '#0f172a' },
textColor: '#94a3b8',
},
grid: {
vertLines: { color: '#1e293b' },
horzLines: { color: '#1e293b' },
},
crosshair: {
mode: 1,
},
rightPriceScale: {
borderColor: '#334155',
},
timeScale: {
borderColor: '#334155',
timeVisible: true,
secondsVisible: false,
},
});
// Add line series
const lineSeries = chart.addLineSeries({
color: '#f59e0b',
lineWidth: 2,
crosshairMarkerVisible: true,
crosshairMarkerRadius: 6,
crosshairMarkerBorderColor: '#f59e0b',
crosshairMarkerBackgroundColor: '#ffffff',
lastValueVisible: true,
priceLineVisible: true,
});
// Add area series for fill
const areaSeries = chart.addAreaSeries({
topColor: 'rgba(245, 158, 11, 0.4)',
bottomColor: 'rgba(245, 158, 11, 0.0)',
lineColor: 'rgba(245, 158, 11, 0.8)',
lineWidth: 2,
});
// Set data
if (data && data.length > 0) {
lineSeries.setData(data);
areaSeries.setData(data);
// Fit content
chart.timeScale().fitContent();
}
chartRef.current = chart;
// Handle resize
const handleResize = () => {
if (chartContainerRef.current && chartRef.current) {
chartRef.current.applyOptions({
width: chartContainerRef.current.clientWidth,
});
}
};
window.addEventListener('resize', handleResize);
return () => {
window.removeEventListener('resize', handleResize);
if (chartRef.current) {
chartRef.current.remove();
chartRef.current = null;
}
};
}, [data]);
// Calculate stats
const stats = data && data.length > 0 ? {
current: data[data.length - 1].value,
start: data[0].value,
change: data[data.length - 1].value - data[0].value,
changePercent: ((data[data.length - 1].value - data[0].value) / data[0].value) * 100,
high: Math.max(...data.map(d => d.value)),
low: Math.min(...data.map(d => d.value)),
} : null;
return (
<div className="bg-gradient-to-br from-slate-900 to-slate-800 rounded-xl p-6 border border-slate-700">
{/* Header */}
<div className="mb-4">
<div className="flex items-center justify-between mb-2">
<div>
<h3 className="text-xl font-bold text-white">{title}</h3>
{model && <p className="text-sm text-slate-400">{model}</p>}
</div>
{stats && (
<div className="text-right">
<div className="text-2xl font-bold text-amber-400">
${stats.current.toLocaleString()}
</div>
<div className={`text-sm font-semibold ${stats.change >= 0 ? 'text-green-400' : 'text-red-400'}`}>
{stats.change >= 0 ? '+' : ''}${stats.change.toLocaleString()} ({stats.changePercent >= 0 ? '+' : ''}{stats.changePercent.toFixed(2)}%)
</div>
</div>
)}
</div>
{/* Stats Bar */}
{stats && (
<div className="grid grid-cols-4 gap-3 mt-4">
<div className="bg-slate-800 rounded-lg p-2 text-center">
<div className="text-xs text-slate-400">Current</div>
<div className="text-sm font-bold text-white">${stats.current.toLocaleString()}</div>
</div>
<div className="bg-slate-800 rounded-lg p-2 text-center">
<div className="text-xs text-slate-400">High</div>
<div className="text-sm font-bold text-green-400">${stats.high.toLocaleString()}</div>
</div>
<div className="bg-slate-800 rounded-lg p-2 text-center">
<div className="text-xs text-slate-400">Low</div>
<div className="text-sm font-bold text-red-400">${stats.low.toLocaleString()}</div>
</div>
<div className="bg-slate-800 rounded-lg p-2 text-center">
<div className="text-xs text-slate-400">Change</div>
<div className={`text-sm font-bold ${stats.changePercent >= 0 ? 'text-green-400' : 'text-red-400'}`}>
{stats.changePercent >= 0 ? '+' : ''}{stats.changePercent.toFixed(1)}%
</div>
</div>
</div>
)}
</div>
{/* Chart */}
<div ref={chartContainerRef} className="w-full" />
{/* Data Source */}
<div className="mt-3 text-xs text-slate-500 text-center">
Data sources: Chrono24, WatchCharts, eBay, WatchBox
</div>
</div>
);
}
// Sample data generator for testing
export function generateSampleData(model = 'Speedmaster Professional') {
const startDate = new Date('2020-01-01');
const dataPoints = [];
let basePrice = 4200;
for (let i = 0; i < 60; i++) {
const date = new Date(startDate);
date.setMonth(date.getMonth() + i);
// Simulate realistic price appreciation
basePrice += (Math.random() - 0.4) * 150; // Slight upward trend
dataPoints.push({
time: date.toISOString().split('T')[0],
value: Math.round(basePrice)
});
}
return dataPoints;
}