← back to Watches
public/price-history.html
342 lines
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Omega Watch Price History</title>
<script src="https://cdn.jsdelivr.net/npm/chart.js@4.5.1/dist/chart.umd.min.js"></script>
<script src="https://cdn.tailwindcss.com"></script>
<style>
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
}
</style>
</head>
<body class="bg-gray-100 min-h-screen">
<div class="max-w-7xl mx-auto p-6 space-y-6">
<div class="bg-white rounded-lg shadow-lg p-6">
<h1 class="text-3xl font-bold text-gray-800 mb-6">Omega Watch Price History</h1>
<!-- Dropdowns -->
<div class="grid grid-cols-1 md:grid-cols-2 gap-4 mb-6">
<div>
<label for="model-select" class="block text-sm font-semibold text-gray-700 mb-2">
Model
</label>
<select
id="model-select"
class="w-full px-4 py-3 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500 text-gray-800 bg-white"
>
<option value="">Loading models...</option>
</select>
</div>
<div>
<label for="year-select" class="block text-sm font-semibold text-gray-700 mb-2">
Year (Show data up to)
</label>
<select
id="year-select"
class="w-full px-4 py-3 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500 text-gray-800 bg-white"
disabled
>
<option value="">Select a model first</option>
</select>
</div>
</div>
<!-- Watch Info -->
<div id="watch-info" class="bg-gray-50 rounded-lg p-4 mb-6 hidden">
<div class="grid grid-cols-1 md:grid-cols-3 gap-4">
<div>
<span class="text-sm text-gray-600">Collection:</span>
<p id="collection" class="font-semibold text-gray-800"></p>
</div>
<div>
<span class="text-sm text-gray-600">Reference:</span>
<p id="reference" class="font-semibold text-gray-800"></p>
</div>
<div>
<span class="text-sm text-gray-600">Year Introduced:</span>
<p id="year-introduced" class="font-semibold text-gray-800"></p>
</div>
</div>
<div id="description-container" class="mt-4 hidden">
<span class="text-sm text-gray-600">Description:</span>
<p id="description" class="text-gray-800 mt-1"></p>
</div>
</div>
<!-- Chart -->
<div class="bg-white rounded-lg p-6 border border-gray-200">
<div class="h-96">
<canvas id="priceChart"></canvas>
</div>
</div>
<!-- Legend Info -->
<div class="bg-blue-50 rounded-lg p-4 mt-4">
<h3 class="font-semibold text-gray-800 mb-2">Chart Information:</h3>
<ul class="space-y-1 text-sm text-gray-700">
<li><span class="font-semibold text-blue-600">Blue Line:</span> Retail prices for new watches</li>
<li><span class="font-semibold text-red-600">Red Line:</span> Confirmed auction sales (vintage/used condition)</li>
<li class="mt-2 text-gray-600">Data shows prices up to the selected year</li>
</ul>
</div>
</div>
</div>
<script type="module">
let watchesData = null;
let chartInstance = null;
const modelSelect = document.getElementById('model-select');
const yearSelect = document.getElementById('year-select');
const watchInfo = document.getElementById('watch-info');
const chartCanvas = document.getElementById('priceChart');
// Load watches data
async function loadWatchesData() {
try {
const response = await fetch('/data/omega-watches-expanded-v3.json');
const data = await response.json();
watchesData = data.watches;
// Populate model dropdown
const watchesList = Object.entries(watchesData).map(([key, value]) => ({
id: key,
...value
}));
modelSelect.innerHTML = '<option value="">Select a model...</option>';
watchesList.forEach(watch => {
const option = document.createElement('option');
option.value = watch.id;
option.textContent = `${watch.name} (${watch.collection})`;
modelSelect.appendChild(option);
});
} catch (error) {
console.error('Error loading watches data:', error);
modelSelect.innerHTML = '<option value="">Error loading data</option>';
}
}
// Update year dropdown when model changes
modelSelect.addEventListener('change', (e) => {
const selectedModelId = e.target.value;
if (!selectedModelId || !watchesData) return;
const watch = watchesData[selectedModelId];
if (!watch || !watch.priceHistory) return;
// Update watch info
document.getElementById('collection').textContent = watch.collection || 'N/A';
document.getElementById('reference').textContent = watch.reference || 'N/A';
document.getElementById('year-introduced').textContent = watch.year_introduced || 'N/A';
if (watch.description) {
document.getElementById('description').textContent = watch.description;
document.getElementById('description-container').classList.remove('hidden');
} else {
document.getElementById('description-container').classList.add('hidden');
}
watchInfo.classList.remove('hidden');
// Get unique years
const years = [...new Set(watch.priceHistory.map(p => p.year))].sort((a, b) => a - b);
yearSelect.innerHTML = '';
years.forEach(year => {
const option = document.createElement('option');
option.value = year.toString();
option.textContent = year.toString();
yearSelect.appendChild(option);
});
yearSelect.disabled = false;
if (years.length > 0) {
yearSelect.value = years[years.length - 1].toString();
updateChart();
}
});
// Update chart when year changes
yearSelect.addEventListener('change', () => {
updateChart();
});
// Update chart
function updateChart() {
const selectedModelId = modelSelect.value;
const selectedYear = parseInt(yearSelect.value);
if (!selectedModelId || !selectedYear || !watchesData) return;
const watch = watchesData[selectedModelId];
if (!watch || !watch.priceHistory) return;
// Filter data up to selected year
const filteredHistory = watch.priceHistory.filter(p => p.year <= selectedYear);
// Separate retail prices (new condition) and auction sales (vintage/other)
const retailPrices = filteredHistory
.filter(p => p.condition === 'new')
.map(p => ({ year: p.year, price: p.price }));
const auctionSales = filteredHistory
.filter(p => p.condition !== 'new' && (p.condition === 'vintage' || p.condition === 'used' || p.condition === 'auction'))
.map(p => ({ year: p.year, price: p.price }));
// Get all unique years
const allYears = [...new Set([...retailPrices.map(p => p.year), ...auctionSales.map(p => p.year)])].sort((a, b) => a - b);
const retailData = allYears.map(year => {
const entry = retailPrices.find(p => p.year === year);
return entry ? entry.price : null;
});
const auctionData = allYears.map(year => {
const entry = auctionSales.find(p => p.year === year);
return entry ? entry.price : null;
});
// Destroy previous chart
if (chartInstance) {
chartInstance.destroy();
}
// Create new chart
chartInstance = new Chart(chartCanvas, {
type: 'line',
data: {
labels: allYears,
datasets: [
{
label: 'Retail Price (New)',
data: retailData,
borderColor: '#2563eb',
backgroundColor: 'rgba(37, 99, 235, 0.1)',
borderWidth: 3,
fill: true,
tension: 0.4,
pointRadius: 6,
pointHoverRadius: 8,
pointBackgroundColor: '#2563eb',
pointBorderColor: '#fff',
pointBorderWidth: 2,
spanGaps: false
},
{
label: 'Auction Sales (Vintage/Used)',
data: auctionData,
borderColor: '#dc2626',
backgroundColor: 'rgba(220, 38, 38, 0.1)',
borderWidth: 3,
fill: true,
tension: 0.4,
pointRadius: 6,
pointHoverRadius: 8,
pointBackgroundColor: '#dc2626',
pointBorderColor: '#fff',
pointBorderWidth: 2,
spanGaps: false
}
]
},
options: {
responsive: true,
maintainAspectRatio: false,
plugins: {
legend: {
display: true,
position: 'top',
labels: {
font: {
size: 14,
weight: 'bold'
},
padding: 15,
usePointStyle: true
}
},
tooltip: {
backgroundColor: 'rgba(0, 0, 0, 0.8)',
titleFont: {
size: 14,
weight: 'bold'
},
bodyFont: {
size: 13
},
padding: 12,
callbacks: {
label: function(context) {
if (context.parsed.y === null) {
return context.dataset.label + ': No data';
}
return context.dataset.label + ': $' + context.parsed.y.toLocaleString();
}
}
},
title: {
display: true,
text: watch.name + ' - Price History',
font: {
size: 18,
weight: 'bold'
},
padding: 20
}
},
scales: {
y: {
beginAtZero: false,
ticks: {
callback: function(value) {
return '$' + value.toLocaleString();
},
font: {
size: 12
}
},
grid: {
color: 'rgba(0, 0, 0, 0.1)'
},
title: {
display: true,
text: 'Price (USD)',
font: {
size: 14,
weight: 'bold'
}
}
},
x: {
ticks: {
font: {
size: 12
}
},
grid: {
display: false
},
title: {
display: true,
text: 'Year',
font: {
size: 14,
weight: 'bold'
}
}
}
}
}
});
}
// Initialize
loadWatchesData();
</script>
</body>
</html>