Files
veola/static/js/price-chart.js
prosolis ea3577a45e Items-list sparklines, retro CSS, pinned tooling, deploy docs
- Bulk-load recent price points per item and render a sparkline in
  the items list (new LoadRecentPriceHistory query avoids N+1).
- Add retro.css visual layer and refreshed login/items/layout styling.
- Swap the logo from webp to avif.
- Pin htmx/Chart.js/Tailwind/templ versions in the Makefile with
  vendor / tools / update-deps targets; README documents the
  dependency-bump flow and the hardened systemd deploy.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-15 19:10:56 -07:00

76 lines
2.4 KiB
JavaScript

// Renders the price-history line chart on the per-item results page.
// Chart data is read from the canvas's data-chart attribute: templ
// interpolates attribute values but treats <script> element contents as raw
// text, so the JSON cannot live in an inline <script> block. Loaded after
// chart.umd.min.js with the #price-chart canvas already in the DOM above it.
(function () {
var canvas = document.getElementById('price-chart');
if (!canvas || !canvas.dataset.chart || typeof Chart === 'undefined') {
return;
}
var data = JSON.parse(canvas.dataset.chart);
var ctx = canvas.getContext('2d');
// Vertical gradient fill — bright accent at the top, transparent at the
// baseline — makes the chart read at a glance instead of as a thin line.
var fill = ctx.createLinearGradient(0, 0, 0, canvas.clientHeight || 200);
fill.addColorStop(0, 'rgba(0, 228, 164, 0.45)');
fill.addColorStop(1, 'rgba(0, 228, 164, 0.00)');
// Chart.js plugin that paints a soft glow under the line stroke before
// the dataset draws. Cheap enough to keep on by default; respects
// prefers-reduced-motion only insofar as nothing animates here.
var glowPlugin = {
id: 'priceLineGlow',
beforeDatasetDraw: function (chart) {
var c = chart.ctx;
c.save();
c.shadowColor = 'rgba(0, 228, 164, 0.55)';
c.shadowBlur = 12;
},
afterDatasetDraw: function (chart) { chart.ctx.restore(); }
};
new Chart(ctx, {
type: 'line',
data: {
labels: data.labels,
datasets: [{
label: 'Best price',
data: data.points,
borderColor: '#00e4a4',
borderWidth: 2.5,
backgroundColor: fill,
pointBackgroundColor: '#ffffff',
pointBorderColor: '#00e4a4',
pointBorderWidth: 1.5,
pointRadius: 3,
pointHoverRadius: 5,
tension: 0.3,
fill: true
}]
},
options: {
responsive: true,
maintainAspectRatio: false,
interaction: { mode: 'index', intersect: false },
scales: {
x: { ticks: { color: '#a8c0f0' }, grid: { color: 'rgba(255,255,255,0.06)' } },
y: { ticks: { color: '#a8c0f0' }, grid: { color: 'rgba(255,255,255,0.06)' } }
},
plugins: {
legend: { labels: { color: '#ffffff' } },
tooltip: {
backgroundColor: 'rgba(20, 32, 80, 0.95)',
borderColor: 'rgba(0, 164, 228, 0.6)',
borderWidth: 1,
titleColor: '#ffffff',
bodyColor: '#a8c0f0',
padding: 10
}
}
},
plugins: [glowPlugin]
});
})();