Fix bugs found in local testing
- Dashboard auto-refresh rendered the full layout into its own refresh container, producing a duplicate sidebar every 60s; it now renders only the body partial. - 'Run Now' runs synchronously with a bounded timeout and returns refreshed results plus success/error feedback, instead of firing-and-forgetting with no signal. - Price-history chart data moved from a <script> block to a data- attribute: templ does not interpolate expressions inside <script> element content, so the JSON was emitted literally. - The htmx indicator spinner was permanently visible due to CSS source order; the indicator rules now follow .v-spinner. Also refreshes README for this session's changes. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -17,6 +17,10 @@ type ItemResultsData struct {
|
||||
TotalPages int
|
||||
Order string
|
||||
HistoryChartJSON string
|
||||
// RunMsg / RunError carry feedback from a "Run Now" poll. Both empty on a
|
||||
// normal page load; PostRunItem sets exactly one.
|
||||
RunMsg string
|
||||
RunError string
|
||||
}
|
||||
|
||||
type BadgeData struct {
|
||||
@@ -61,8 +65,17 @@ templ itemResultsBody(d ItemResultsData) {
|
||||
<span class={ "v-badge", d.Badge.Class }>{ d.Badge.Label }</span>
|
||||
</div>
|
||||
}
|
||||
<form class="mt-3" hx-post={ fmt.Sprintf("/items/%d/run", d.Item.ID) } hx-swap="none">
|
||||
<form
|
||||
class="mt-3 flex items-center gap-2 justify-end"
|
||||
hx-post={ fmt.Sprintf("/items/%d/run", d.Item.ID) }
|
||||
hx-target="#item-results-table"
|
||||
hx-swap="outerHTML"
|
||||
hx-disabled-elt="find button"
|
||||
>
|
||||
<input type="hidden" name="csrf_token" value={ d.CSRFToken }/>
|
||||
<input type="hidden" name="from" value="results"/>
|
||||
<span class="v-spinner htmx-indicator"></span>
|
||||
<span class="v-muted text-sm htmx-indicator">Running...</span>
|
||||
<button class="v-btn" type="submit">Run Now</button>
|
||||
</form>
|
||||
</div>
|
||||
@@ -73,41 +86,30 @@ templ itemResultsBody(d ItemResultsData) {
|
||||
if len(d.History) < 2 {
|
||||
<div class="v-muted">Not enough history yet.</div>
|
||||
} else {
|
||||
<canvas id="price-chart" height="120"></canvas>
|
||||
// Chart data rides on a data- attribute, not <script> content:
|
||||
// templ interpolates (and escapes) attribute values but treats
|
||||
// <script> bodies as raw text, leaving { expr } literal.
|
||||
<canvas id="price-chart" height="120" data-chart={ d.HistoryChartJSON }></canvas>
|
||||
<script src="/static/vendor/chart.umd.min.js"></script>
|
||||
<script id="price-data" type="application/json">{ d.HistoryChartJSON }</script>
|
||||
<script>
|
||||
(function(){
|
||||
var data = JSON.parse(document.getElementById('price-data').textContent);
|
||||
var ctx = document.getElementById('price-chart').getContext('2d');
|
||||
new Chart(ctx, {
|
||||
type: 'line',
|
||||
data: {
|
||||
labels: data.labels,
|
||||
datasets: [{
|
||||
label: 'Best price',
|
||||
data: data.points,
|
||||
borderColor: '#00e4a4',
|
||||
backgroundColor: 'rgba(0,228,164,0.15)',
|
||||
pointBackgroundColor: '#e84040',
|
||||
pointRadius: 3,
|
||||
tension: 0.25,
|
||||
fill: true
|
||||
}]
|
||||
},
|
||||
options: {
|
||||
scales: {
|
||||
x: { ticks: { color: '#a8c0f0' }, grid: { color: 'rgba(255,255,255,0.07)' } },
|
||||
y: { ticks: { color: '#a8c0f0' }, grid: { color: 'rgba(255,255,255,0.07)' } }
|
||||
},
|
||||
plugins: { legend: { labels: { color: '#ffffff' } } }
|
||||
}
|
||||
});
|
||||
})();
|
||||
</script>
|
||||
<script src="/static/js/price-chart.js"></script>
|
||||
}
|
||||
</div>
|
||||
|
||||
@ItemResultsTable(d)
|
||||
</div>
|
||||
}
|
||||
|
||||
// ItemResultsTable is the results listing + pagination, plus optional "Run
|
||||
// Now" feedback. It is both part of the initial page (via itemResultsBody)
|
||||
// and the standalone response to POST /items/{id}/run from the results page,
|
||||
// so the Run Now button targets #item-results-table with hx-swap="outerHTML".
|
||||
templ ItemResultsTable(d ItemResultsData) {
|
||||
<div id="item-results-table">
|
||||
if d.RunError != "" {
|
||||
<div class="v-flash-error">{ d.RunError }</div>
|
||||
} else if d.RunMsg != "" {
|
||||
<div class="v-flash">{ d.RunMsg }</div>
|
||||
}
|
||||
<div class="v-card p-0 overflow-hidden">
|
||||
<table class="v-table">
|
||||
<thead>
|
||||
|
||||
Reference in New Issue
Block a user