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.

111 lines
3.3 KiB

package templates
import "track-gopher/derby"
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>
<link href="/static/css/tailwind.css" rel="stylesheet" />
<script src="/static/js/htmx.min.js"></script>
</head>
<body class="bg-gray-100 min-h-screen">
<div class="container mx-auto px-4 py-8">
<header class="mb-8">
<h1 class="text-3xl font-bold text-center">Derby Race Timer</h1>
</header>
<div class="mb-6 flex justify-center">
<div id="race-status" class="px-4 py-2 rounded-full text-white bg-blue-600">Ready</div>
</div>
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-4 mb-8">
{ laneComponents() }
</div>
<div class="flex justify-center space-x-4">
<button
hx-post="/api/reset"
hx-swap="none"
class="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded">
Reset Race
</button>
<button
hx-post="/api/force-end"
hx-swap="none"
class="bg-red-500 hover:bg-red-700 text-white font-bold py-2 px-4 rounded">
Force End Race
</button>
</div>
</div>
<script>
// Set up SSE connection
const eventSource = new EventSource('/api/events');
eventSource.addEventListener('lane-finish', function(event) {
const laneFinishData = JSON.parse(event.data);
const lane = document.getElementById(`lane-${laneFinishData.lane}`);
if (lane) {
lane.classList.add('finished');
lane.querySelector('.time').textContent = laneFinishData.time.toFixed(4);
const placeEl = lane.querySelector('.place');
placeEl.textContent = `${getOrdinal(laneFinishData.place)} Place`;
placeEl.classList.remove('hidden');
}
});
eventSource.addEventListener('status', function(event) {
const statusData = JSON.parse(event.data);
let statusText = 'Unknown';
let statusClass = 'bg-gray-500';
if (statusData.status === 'idle') {
statusText = 'Ready';
statusClass = 'bg-blue-600';
// Reset all lanes
document.querySelectorAll('.lane').forEach(lane => {
lane.classList.remove('finished');
lane.querySelector('.time').textContent = '--.--.---';
lane.querySelector('.place').classList.add('hidden');
});
} else if (statusData.status === 'running') {
statusText = 'Race Running';
statusClass = 'bg-green-600';
} else if (statusData.status === 'finished') {
statusText = 'Race Complete';
statusClass = 'bg-purple-600';
}
document.getElementById('race-status').textContent = statusText;
document.getElementById('race-status').className = `px-4 py-2 rounded-full text-white ${statusClass}`;
});
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 id={ "lane-" + string(rune('0' + lane)) } class="lane bg-white p-4 rounded shadow">
<h2 class="text-xl font-semibold mb-2">Lane { string(rune('0' + lane)) }</h2>
<div class="text-3xl font-mono mb-2 time">--.--.---</div>
<div class="place text-lg font-bold text-green-600 hidden"></div>
</div>
}