← back to Handbag Authentication

public/js/price-tracker.js

292 lines

// Retail Price Tracking Data (manually curated from official brand price increases)
const retailPriceHistory = {
  'Hermes': {
    'Birkin 30': {
      2020: 9850,
      2021: 10300,
      2022: 10800,
      2023: 11200,
      2024: 11200
    },
    'Kelly 28': {
      2020: 9400,
      2021: 9850,
      2022: 10300,
      2023: 10600,
      2024: 10600
    },
    'Constance 18': {
      2020: 7500,
      2021: 7950,
      2022: 8300,
      2023: 8500,
      2024: 8500
    }
  },
  'Chanel': {
    'Classic Flap Medium': {
      2020: 6800,
      2021: 7400,
      2022: 8800,
      2023: 9800,
      2024: 10200
    },
    'Boy Bag Medium': {
      2020: 4900,
      2021: 5400,
      2022: 6400,
      2023: 7000,
      2024: 7400
    },
    'Chanel 19 Medium': {
      2020: 5600,
      2021: 6200,
      2022: 7200,
      2023: 7600,
      2024: 7900
    }
  },
  'Louis Vuitton': {
    'Neverfull MM': {
      2020: 1500,
      2021: 1630,
      2022: 1760,
      2023: 1900,
      2024: 2030
    },
    'Speedy 30': {
      2020: 1140,
      2021: 1240,
      2022: 1350,
      2023: 1760,
      2024: 1900
    },
    'Pochette Metis': {
      2020: 2050,
      2021: 2230,
      2022: 2420,
      2023: 2630,
      2024: 2840
    }
  },
  'Gucci': {
    'Marmont Medium': {
      2020: 2490,
      2021: 2590,
      2022: 2790,
      2023: 2890,
      2024: 2980
    },
    'Dionysus Medium': {
      2020: 2790,
      2021: 2890,
      2022: 3050,
      2023: 3150,
      2024: 3200
    }
  },
  'Prada': {
    'Galleria Medium': {
      2020: 2950,
      2021: 3150,
      2022: 3450,
      2023: 3650,
      2024: 3750
    },
    'Re-Edition 2005': {
      2020: 890,
      2021: 995,
      2022: 1150,
      2023: 1250,
      2024: 1350
    }
  }
};

let currentBrand = 'Hermes';
let priceChart = null;

// Initialize
document.addEventListener('DOMContentLoaded', () => {
  setupBrandTabs();
  renderBrand('Hermes');
});

function setupBrandTabs() {
  document.querySelectorAll('.sort-tab').forEach(tab => {
    tab.addEventListener('click', () => {
      document.querySelectorAll('.sort-tab').forEach(t => t.classList.remove('active'));
      tab.classList.add('active');
      currentBrand = tab.getAttribute('data-brand');
      renderBrand(currentBrand);
    });
  });
}

function renderBrand(brand) {
  const data = retailPriceHistory[brand];
  if (!data) return;

  renderSummary(brand, data);
  renderChart(brand, data);
  renderTable(brand, data);
  renderInsights(brand, data);
}

function renderSummary(brand, data) {
  const summaryEl = document.getElementById('summary-content');
  let html = '';

  for (const [model, prices] of Object.entries(data)) {
    const years = Object.keys(prices).sort();
    const firstYear = years[0];
    const lastYear = years[years.length - 1];
    const firstPrice = prices[firstYear];
    const lastPrice = prices[lastYear];
    const increase = lastPrice - firstPrice;
    const increasePercent = ((increase / firstPrice) * 100).toFixed(1);

    html += `
      <div style="background: linear-gradient(135deg, #f5f7fa 0%, #c3cfe2 100%); padding: 1.5rem; border-radius: 10px;">
        <h3 style="margin-top: 0; font-size: 1.1rem;">${brand} ${model}</h3>
        <div style="font-size: 2rem; font-weight: 700; color: #667eea; margin: 0.5rem 0;">
          +${increasePercent}%
        </div>
        <div style="font-size: 0.9rem; color: #7f8c8d;">
          $${firstPrice.toLocaleString()} (${firstYear}) → $${lastPrice.toLocaleString()} (${lastYear})
        </div>
        <div style="font-size: 0.85rem; margin-top: 0.5rem; font-weight: 600; color: #27ae60;">
          +$${increase.toLocaleString()} increase
        </div>
      </div>
    `;
  }

  summaryEl.innerHTML = html;
}

function renderChart(brand, data) {
  const ctx = document.getElementById('priceChart').getContext('2d');

  const datasets = [];
  const colors = ['#667eea', '#764ba2', '#f093fb', '#f5576c', '#fa709a', '#fee140'];

  let i = 0;
  for (const [model, prices] of Object.entries(data)) {
    datasets.push({
      label: `${brand} ${model}`,
      data: Object.values(prices),
      borderColor: colors[i % colors.length],
      backgroundColor: colors[i % colors.length] + '20',
      tension: 0.3,
      fill: true
    });
    i++;
  }

  const years = Object.keys(Object.values(data)[0]);

  if (priceChart) {
    priceChart.destroy();
  }

  priceChart = new Chart(ctx, {
    type: 'line',
    data: {
      labels: years,
      datasets: datasets
    },
    options: {
      responsive: true,
      maintainAspectRatio: true,
      plugins: {
        title: {
          display: true,
          text: `${brand} Retail Price Growth (2020-2024)`,
          font: { size: 16, weight: 'bold' }
        },
        legend: {
          display: true,
          position: 'top'
        }
      },
      scales: {
        y: {
          beginAtZero: false,
          ticks: {
            callback: function(value) {
              return '$' + value.toLocaleString();
            }
          }
        }
      }
    }
  });
}

function renderTable(brand, data) {
  const tbody = document.getElementById('model-table-body');
  let html = '';

  for (const [model, prices] of Object.entries(data)) {
    const years = Object.keys(prices).sort();
    const firstPrice = prices[years[0]];
    const lastPrice = prices[years[years.length - 1]];
    const totalIncrease = lastPrice - firstPrice;
    const totalIncreasePercent = ((totalIncrease / firstPrice) * 100).toFixed(1);
    const annualGrowth = (totalIncreasePercent / (years.length - 1)).toFixed(1);

    html += `
      <tr>
        <td style="font-weight: 700;">${brand} ${model}</td>
        ${years.map(year => `<td>$${prices[year].toLocaleString()}</td>`).join('')}
        <td style="color: #27ae60; font-weight: 700;">+${totalIncreasePercent}%</td>
        <td style="color: #667eea; font-weight: 700;">${annualGrowth}%/yr</td>
      </tr>
    `;
  }

  tbody.innerHTML = html;
}

function renderInsights(brand, data) {
  const insightsEl = document.getElementById('insights');

  // Calculate average annual growth
  let totalGrowth = 0;
  let modelCount = 0;

  for (const [model, prices] of Object.entries(data)) {
    const years = Object.keys(prices).sort();
    const firstPrice = prices[years[0]];
    const lastPrice = prices[years[years.length - 1]];
    const growthPercent = ((lastPrice - firstPrice) / firstPrice) * 100;
    const annualGrowth = growthPercent / (years.length - 1);
    totalGrowth += annualGrowth;
    modelCount++;
  }

  const avgAnnualGrowth = (totalGrowth / modelCount).toFixed(1);

  // Find best performer
  let bestModel = '';
  let bestGrowth = 0;
  for (const [model, prices] of Object.entries(data)) {
    const years = Object.keys(prices).sort();
    const firstPrice = prices[years[0]];
    const lastPrice = prices[years[years.length - 1]];
    const growthPercent = ((lastPrice - firstPrice) / firstPrice) * 100;
    if (growthPercent > bestGrowth) {
      bestGrowth = growthPercent;
      bestModel = model;
    }
  }

  insightsEl.innerHTML = `
    <p><strong>✅ Average Annual Growth:</strong> ${brand} bags increase an average of <strong>${avgAnnualGrowth}% per year</strong> at retail.</p>
    <p><strong>🏆 Best Performer:</strong> The ${bestModel} has appreciated <strong>+${bestGrowth.toFixed(1)}%</strong> since 2020.</p>
    <p><strong>💎 Investment Strategy:</strong> Buying ${brand} bags from Japan at 20-40% below US retail means you're getting bags at 2018-2019 prices, with built-in appreciation.</p>
    <p><strong>📈 Arbitrage Edge:</strong> Even if you buy today and resell in 2 years, retail price increases alone could cover your profit margin!</p>
  `;
}