DustyP 9 months ago
parent 8c9e9046ad
commit c576b07a66

@ -144,108 +144,102 @@ templ RaceManage(groups []models.Group, currentGroup models.Group, heats []model
</div> </div>
<script> <script>
// WebSocket connections // Set up SSE connection
const timerSocket = new WebSocket(`${window.location.protocol === 'https:' ? 'wss:' : 'ws:'}//${window.location.host}/ws/timer`); console.log("Setting up SSE connection...");
const adminSocket = new WebSocket(`${window.location.protocol === 'https:' ? 'wss:' : 'ws:'}//${window.location.host}/ws/admin`);
const eventsSocket = new WebSocket(`${window.location.protocol === 'https:' ? 'wss:' : 'ws:'}//${window.location.host}/events`);
const timerDisplay = document.getElementById('timer'); const eventSource = new EventSource('/api/events');
const statusIndicator = document.getElementById('status-indicator');
const gateStatus = document.getElementById('gate-status');
const gateStatusText = document.getElementById('gate-status-text');
// Timer socket handling eventSource.onopen = function() {
timerSocket.onmessage = function(event) { console.log("SSE connection opened");
const data = JSON.parse(event.data); };
if (data.type === 'time') { eventSource.onerror = function(error) {
timerDisplay.textContent = data.time.toFixed(3); console.error("SSE connection error:", error);
} else if (data.type === 'status') { };
statusIndicator.textContent = data.status;
// Update status indicator color
statusIndicator.className = 'badge mb-3 ';
if (data.status === 'Ready') {
statusIndicator.className += 'bg-secondary';
} else if (data.status === 'Running') {
statusIndicator.className += 'bg-success';
} else if (data.status === 'Finished') {
statusIndicator.className += 'bg-primary';
// Auto-save results when race finishes
saveHeatResults();
}
} else if (data.type === 'gate') {
gateStatusText.textContent = data.status;
// Update gate status color eventSource.addEventListener('debug', function(event) {
gateStatus.className = 'alert '; console.log("Debug event received:", event.data);
if (data.status === 'Open') { });
gateStatus.className += 'alert-danger';
} else if (data.status === 'Closed') { eventSource.addEventListener('lane-finish', function(event) {
gateStatus.className += 'alert-success'; console.log("Lane finish event received:", event.data);
} else { try {
gateStatus.className += 'alert-secondary'; const laneFinishData = JSON.parse(event.data);
} const laneTimeElement = document.getElementById(`lane-${laneFinishData.lane}-time`);
} else if (data.type === 'lane-time') {
// Update lane time display
const laneTimeElement = document.getElementById(`lane-${data.lane}-time`);
if (laneTimeElement) { if (laneTimeElement) {
laneTimeElement.textContent = data.time.toFixed(3); laneTimeElement.textContent = laneFinishData.time.toFixed(4);
} }
} else if (data.type === 'lane-position') {
// Update lane position display const lanePositionElement = document.getElementById(`lane-${laneFinishData.lane}-position`);
const lanePositionElement = document.getElementById(`lane-${data.lane}-position`);
if (lanePositionElement) { if (lanePositionElement) {
lanePositionElement.textContent = data.position; lanePositionElement.textContent = laneFinishData.place;
} }
} catch (error) {
console.error("Error processing lane finish event:", error);
} }
}; });
// Admin socket handling eventSource.addEventListener('status', function(event) {
adminSocket.onmessage = function(event) { console.log("Status event received:", event.data);
const data = JSON.parse(event.data); try {
const statusData = JSON.parse(event.data);
let statusText = 'Unknown';
let statusClass = 'bg-secondary';
if (data === 'heat-changed' || data === 'group-changed' || data === 'heat-rerun') { if (statusData.status === 'idle') {
// Reload the page when heat or group changes statusText = 'Ready';
window.location.reload(); statusClass = 'bg-primary';
}
};
// Events socket handling // Reset all times and positions
eventsSocket.onmessage = function(event) { document.querySelectorAll('[id^="lane-"][id$="-time"]').forEach(el => {
const data = JSON.parse(event.data); el.textContent = '--.-';
});
if (data.event === 'race_start') { document.querySelectorAll('[id^="lane-"][id$="-position"]').forEach(el => {
// Race has started el.textContent = '-';
console.log('Race started!'); });
// Update UI or play sound if needed
} else if (data.event === 'lane_finish') {
// A lane has finished
console.log(`Lane ${data.lane} finished with time ${data.time}`);
// Update the lane time and position in the UI } else if (statusData.status === 'running') {
const laneTimeElement = document.getElementById(`lane-${data.lane}-time`); statusText = 'Race Running';
if (laneTimeElement) { statusClass = 'bg-success';
laneTimeElement.textContent = data.time.toFixed(3); } else if (statusData.status === 'finished') {
statusText = 'Race Complete';
statusClass = 'bg-info';
// Save heat results
saveHeatResults();
} }
const lanePositionElement = document.getElementById(`lane-${data.lane}-position`); const statusIndicator = document.getElementById('status-indicator');
if (lanePositionElement) { if (statusIndicator) {
lanePositionElement.textContent = data.position; statusIndicator.textContent = statusText;
statusIndicator.className = `badge mb-3 ${statusClass}`;
} }
} else if (data.event === 'race_end') {
// Race has ended
console.log('Race ended!');
// Save the heat results const timerDisplay = document.getElementById('timer');
saveHeatResults(); if (timerDisplay && statusData.status === 'idle') {
timerDisplay.textContent = '0.000';
}
} catch (error) {
console.error("Error processing status event:", error);
}
});
// Admin socket for admin-specific events
const adminSocket = new WebSocket(`${window.location.protocol === 'https:' ? 'wss:' : 'ws:'}//${window.location.host}/ws/admin`);
adminSocket.onmessage = function(event) {
const data = JSON.parse(event.data);
if (data === 'heat-changed' || data === 'group-changed' || data === 'heat-rerun') {
// Reload the page when heat or group changes
window.location.reload();
} }
}; };
// Function to reset the timer // Function to reset the timer
function resetTimer() { function resetTimer() {
fetch('/api/timer/reset', { method: 'POST' }) fetch('/api/reset', { method: 'POST' })
.then(response => { .then(response => {
if (!response.ok) { if (!response.ok) {
console.error('Failed to reset timer'); console.error('Failed to reset timer');
@ -258,7 +252,7 @@ templ RaceManage(groups []models.Group, currentGroup models.Group, heats []model
// Function to force end the current heat // Function to force end the current heat
function forceEndHeat() { function forceEndHeat() {
fetch('/api/timer/force-end', { method: 'POST' }) fetch('/api/force-end', { method: 'POST' })
.then(response => { .then(response => {
if (!response.ok) { if (!response.ok) {
console.error('Failed to force end heat'); console.error('Failed to force end heat');
@ -308,6 +302,12 @@ templ RaceManage(groups []models.Group, currentGroup models.Group, heats []model
console.error('Error:', error); console.error('Error:', 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> </script>
} }
} }

File diff suppressed because one or more lines are too long

@ -69,71 +69,105 @@ templ RacePublic(currentGroup models.Group, heats []models.Heat, racers []models
</div> </div>
<script> <script>
// WebSocket connection for timer updates // Set up SSE connection
const timerSocket = new WebSocket(`${window.location.protocol === 'https:' ? 'wss:' : 'ws:'}//${window.location.host}/ws/timer`); console.log("Setting up SSE connection...");
const timerDisplay = document.getElementById('timer');
const statusIndicator = document.getElementById('status-indicator'); const eventSource = new EventSource('/api/events');
eventSource.onopen = function() {
console.log("SSE connection opened");
};
eventSource.onerror = function(error) {
console.error("SSE connection error:", error);
};
timerSocket.onmessage = function(event) { eventSource.addEventListener('debug', function(event) {
const data = JSON.parse(event.data); console.log("Debug event received:", event.data);
});
if (data.type === 'time') {
timerDisplay.textContent = data.time.toFixed(3); eventSource.addEventListener('lane-finish', function(event) {
} else if (data.type === 'status') { console.log("Lane finish event received:", event.data);
statusIndicator.textContent = data.status; try {
const laneFinishData = JSON.parse(event.data);
// Update status indicator color const laneTimeElement = document.getElementById(`lane-${laneFinishData.lane}-time`);
statusIndicator.className = 'badge ';
if (data.status === 'Ready') {
statusIndicator.className += 'bg-secondary';
} else if (data.status === 'Running') {
statusIndicator.className += 'bg-success';
} else if (data.status === 'Finished') {
statusIndicator.className += 'bg-primary';
}
} else if (data.type === 'lane-time') {
// Update lane time display
const laneTimeElement = document.getElementById(`lane-${data.lane}-time`);
if (laneTimeElement) { if (laneTimeElement) {
laneTimeElement.textContent = data.time.toFixed(3); laneTimeElement.textContent = laneFinishData.time.toFixed(4);
} }
} else if (data.type === 'lane-position') {
// Update lane position display const lanePositionElement = document.getElementById(`lane-${laneFinishData.lane}-position`);
const lanePositionElement = document.getElementById(`lane-${data.lane}-position`);
if (lanePositionElement) { if (lanePositionElement) {
lanePositionElement.textContent = data.position; lanePositionElement.textContent = getOrdinal(laneFinishData.place);
} }
} else if (data.type === 'reload') {
// Reload the page when heat changes // Highlight the lane card
window.location.reload(); const laneCard = document.querySelector(`.lane-card[data-lane="${laneFinishData.lane}"]`);
if (laneCard) {
laneCard.classList.add('bg-success-subtle');
} }
}; } 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';
// Events WebSocket for race events if (statusData.status === 'idle') {
const eventsSocket = new WebSocket(`${window.location.protocol === 'https:' ? 'wss:' : 'ws:'}//${window.location.host}/events`); statusText = 'Ready';
statusClass = 'bg-primary';
eventsSocket.onmessage = function(event) {
const data = JSON.parse(event.data); // Reset all lanes
document.querySelectorAll('.lane-card').forEach(lane => {
if (data.event === 'race_start') { lane.classList.remove('bg-success-subtle');
// Race has started });
console.log('Race started!');
// You could add visual effects or sounds here // Reset all times and positions
} else if (data.event === 'lane_finish') { document.querySelectorAll('[id^="lane-"][id$="-time"]').forEach(el => {
// A lane has finished el.textContent = '--.-';
console.log(`Lane ${data.lane} finished with time ${data.time}`); });
// You could add visual effects or sounds here
} else if (data.event === 'race_end') { document.querySelectorAll('[id^="lane-"][id$="-position"]').forEach(el => {
// Race has ended el.textContent = '-';
console.log('Race ended!'); });
// You could add visual effects or sounds here
} else if (statusData.status === 'running') {
statusText = 'Race Running';
statusClass = 'bg-success';
} else if (statusData.status === 'finished') {
statusText = 'Race Complete';
statusClass = 'bg-info';
// Reload the page after a short delay to show final results // Reload the page after a short delay to show final results
setTimeout(() => { setTimeout(() => {
window.location.reload(); window.location.reload();
}, 3000); }, 3000);
} }
};
const statusIndicator = document.getElementById('status-indicator');
if (statusIndicator) {
statusIndicator.textContent = statusText;
statusIndicator.className = `badge ${statusClass}`;
}
const timerDisplay = document.getElementById('timer');
if (timerDisplay && statusData.status === 'idle') {
timerDisplay.textContent = '0.000';
}
} 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]);
}
// Auto-refresh the page every 30 seconds to keep data current // Auto-refresh the page every 30 seconds to keep data current
setTimeout(() => { setTimeout(() => {
@ -218,7 +252,7 @@ templ raceLaneCard(lane int, racerID int64, racers []models.Racer, result *model
}} }}
<div class="col"> <div class="col">
<div class="card h-100 lane-card"> <div class="card h-100 lane-card" data-lane="{ strconv.Itoa(lane) }">
<div class="card-header bg-primary text-white"> <div class="card-header bg-primary text-white">
<h4 class="mb-0">Lane { strconv.Itoa(lane) }</h4> <h4 class="mb-0">Lane { strconv.Itoa(lane) }</h4>
</div> </div>

File diff suppressed because one or more lines are too long
Loading…
Cancel
Save