You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

144 lines
4.3 KiB

package templates
templ Index() {
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Derby Race Timer</title>
<!-- Bootstrap CSS -->
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" rel="stylesheet">
<script src="/static/js/htmx.min.js"></script>
<!-- Bootstrap JS Bundle with Popper -->
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.bundle.min.js"></script>
</head>
<body class="bg-light">
<div class="container py-4">
<header class="mb-4">
<h1 class="text-center">Derby Race Timer</h1>
</header>
<div class="mb-4 text-center">
<div id="race-status" class="badge bg-primary fs-5 px-3 py-2">Ready</div>
</div>
<div class="row row-cols-1 row-cols-md-2 row-cols-lg-4 g-4 mb-4">
@laneComponents()
</div>
<div class="d-flex justify-content-center gap-3">
<button
hx-post="/api/reset"
hx-swap="none"
class="btn btn-primary">
Reset Race
</button>
<button
hx-post="/api/force-end"
hx-swap="none"
class="btn btn-danger">
Force End Race
</button>
</div>
</div>
<script>
// Set up SSE connection
console.log("Setting up SSE connection...");
const eventSource = new EventSource('/api/events');
eventSource.onopen = function() {
console.log("SSE connection opened");
};
eventSource.onerror = function(error) {
console.error("SSE connection error:", error);
};
eventSource.addEventListener('debug', function(event) {
console.log("Debug event received:", event.data);
});
eventSource.addEventListener('lane-finish', function(event) {
console.log("Lane finish event received:", event.data);
try {
const laneFinishData = JSON.parse(event.data);
const lane = document.getElementById(`lane-${laneFinishData.lane}`);
if (lane) {
console.log(`Updating lane ${laneFinishData.lane} with time ${laneFinishData.time}`);
lane.classList.add('bg-success-subtle');
lane.querySelector('.time').textContent = laneFinishData.time.toFixed(4);
const placeEl = lane.querySelector('.place');
placeEl.textContent = `${getOrdinal(laneFinishData.place)} Place`;
placeEl.classList.remove('d-none');
} else {
console.error(`Lane element not found for lane ${laneFinishData.lane}`);
}
} catch (error) {
console.error("Error processing lane finish event:", error);
}
});
eventSource.addEventListener('status', function(event) {
console.log("Status event received:", event.data);
try {
const statusData = JSON.parse(event.data);
let statusText = 'Unknown';
let statusClass = 'bg-secondary';
if (statusData.status === 'idle') {
statusText = 'Ready';
statusClass = 'bg-primary';
// Reset all lanes
document.querySelectorAll('.lane').forEach(lane => {
lane.classList.remove('bg-success-subtle');
lane.querySelector('.time').textContent = '--.--.---';
lane.querySelector('.place').classList.add('d-none');
});
} else if (statusData.status === 'running') {
statusText = 'Race Running';
statusClass = 'bg-success';
} else if (statusData.status === 'finished') {
statusText = 'Race Complete';
statusClass = 'bg-info';
}
const statusEl = document.getElementById('race-status');
statusEl.textContent = statusText;
statusEl.className = `badge fs-5 px-3 py-2 ${statusClass}`;
} catch (error) {
console.error("Error processing status event:", error);
}
});
function getOrdinal(n) {
const s = ["th", "st", "nd", "rd"];
const v = n % 100;
return n + (s[(v-20)%10] || s[v] || s[0]);
}
</script>
</body>
</html>
}
templ laneComponents() {
for i := 1; i <= 4; i++ {
@laneComponent(i)
}
}
templ laneComponent(lane int) {
<div class="col">
<div id={ "lane-" + string(rune('0' + lane)) } class="lane card h-100">
<div class="card-body text-center">
<h5 class="card-title">Lane { string(rune('0' + lane)) }</h5>
<div class="time display-6 font-monospace my-3">--.--.---</div>
<div class="place badge bg-success fs-6 d-none"></div>
</div>
</div>
</div>
}