parent
e32eb58500
commit
61f667ab0c
@ -0,0 +1,449 @@
|
|||||||
|
package templates
|
||||||
|
|
||||||
|
import (
|
||||||
|
"track-gopher/models"
|
||||||
|
"fmt"
|
||||||
|
"strconv"
|
||||||
|
)
|
||||||
|
|
||||||
|
// RaceManage renders the race management view
|
||||||
|
templ RaceManage(groups []models.Group, currentGroup models.Group, heats []models.Heat, racers []models.Racer, currentHeatNum int, results []models.HeatResult) {
|
||||||
|
@Layout("Race Management") {
|
||||||
|
<div class="container-fluid mt-3">
|
||||||
|
<div class="row mb-4">
|
||||||
|
<div class="col-md-6">
|
||||||
|
<div class="card">
|
||||||
|
<div class="card-header bg-primary text-white">
|
||||||
|
<h3 class="mb-0">Race Controls</h3>
|
||||||
|
</div>
|
||||||
|
<div class="card-body">
|
||||||
|
<div class="mb-3">
|
||||||
|
<label for="group-select" class="form-label">Racing Group</label>
|
||||||
|
<select id="group-select" class="form-select" hx-post="/api/race/set-group" hx-trigger="change" hx-vals='js:{"group_id": parseInt(this.value)}' hx-swap="none">
|
||||||
|
for _, group := range groups {
|
||||||
|
<option value={ fmt.Sprintf("%d", group.ID) } selected?={ group.ID == currentGroup.ID }>
|
||||||
|
{ group.Name }
|
||||||
|
</option>
|
||||||
|
}
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="d-flex justify-content-between mb-3">
|
||||||
|
<button id="prev-heat-btn" class="btn btn-secondary" hx-post="/api/race/previous-heat" hx-swap="none" disabled?={ currentHeatNum <= 1 }>
|
||||||
|
<i class="bi bi-arrow-left"></i> Previous Heat
|
||||||
|
</button>
|
||||||
|
<span class="align-self-center">
|
||||||
|
<strong>Heat { strconv.Itoa(currentHeatNum) } of { strconv.Itoa(len(heats)) }</strong>
|
||||||
|
</span>
|
||||||
|
<button id="next-heat-btn" class="btn btn-secondary" hx-post="/api/race/next-heat" hx-swap="none" disabled?={ currentHeatNum >= len(heats) }>
|
||||||
|
Next Heat <i class="bi bi-arrow-right"></i>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="d-flex justify-content-between">
|
||||||
|
<button id="reset-timer-btn" class="btn btn-warning" onclick="resetTimer()">
|
||||||
|
<i class="bi bi-arrow-clockwise"></i> Reset Timer
|
||||||
|
</button>
|
||||||
|
<button id="force-end-btn" class="btn btn-danger" onclick="forceEndHeat()">
|
||||||
|
<i class="bi bi-flag-fill"></i> Force End Heat
|
||||||
|
</button>
|
||||||
|
<button id="rerun-heat-btn" class="btn btn-info" hx-post="/api/race/rerun-heat" hx-swap="none">
|
||||||
|
<i class="bi bi-arrow-repeat"></i> Re-run Heat
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="col-md-6">
|
||||||
|
<div class="card">
|
||||||
|
<div class="card-header bg-primary text-white">
|
||||||
|
<h3 class="mb-0">Timer</h3>
|
||||||
|
</div>
|
||||||
|
<div class="card-body">
|
||||||
|
<div class="timer-display text-center">
|
||||||
|
<div id="timer" class="display-1 fw-bold">0.000</div>
|
||||||
|
<div id="status-indicator" class="badge bg-secondary mb-3">Ready</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="d-flex justify-content-center">
|
||||||
|
<div id="gate-status" class="alert alert-secondary">
|
||||||
|
Gate Status: <span id="gate-status-text">Unknown</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-12">
|
||||||
|
<div class="card mb-4">
|
||||||
|
<div class="card-header bg-primary text-white">
|
||||||
|
<h3 class="mb-0">Current Heat</h3>
|
||||||
|
</div>
|
||||||
|
<div class="card-body">
|
||||||
|
<div id="current-heat">
|
||||||
|
@raceManageCurrentHeat(heats, racers, currentHeatNum, results)
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-md-6">
|
||||||
|
<div class="card mb-4">
|
||||||
|
<div class="card-header bg-info text-white">
|
||||||
|
<h3 class="mb-0">Next Heat</h3>
|
||||||
|
</div>
|
||||||
|
<div class="card-body">
|
||||||
|
if currentHeatNum < len(heats) {
|
||||||
|
@raceManageNextHeat(heats, racers, currentHeatNum+1)
|
||||||
|
} else {
|
||||||
|
<div class="alert alert-info">No more heats in this group</div>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="col-md-6">
|
||||||
|
<div class="card mb-4">
|
||||||
|
<div class="card-header bg-secondary text-white">
|
||||||
|
<h3 class="mb-0">Heat Results</h3>
|
||||||
|
</div>
|
||||||
|
<div class="card-body">
|
||||||
|
<div class="table-responsive">
|
||||||
|
<table class="table table-striped table-sm">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Heat</th>
|
||||||
|
<th>Lane 1</th>
|
||||||
|
<th>Lane 2</th>
|
||||||
|
<th>Lane 3</th>
|
||||||
|
<th>Lane 4</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
for _, result := range results {
|
||||||
|
<tr>
|
||||||
|
<td>{ strconv.Itoa(result.HeatNumber) }</td>
|
||||||
|
<td>{ fmt.Sprintf("%.3f (#%d)", result.Lane1Time, result.Lane1Position) }</td>
|
||||||
|
<td>{ fmt.Sprintf("%.3f (#%d)", result.Lane2Time, result.Lane2Position) }</td>
|
||||||
|
<td>{ fmt.Sprintf("%.3f (#%d)", result.Lane3Time, result.Lane3Position) }</td>
|
||||||
|
<td>{ fmt.Sprintf("%.3f (#%d)", result.Lane4Time, result.Lane4Position) }</td>
|
||||||
|
</tr>
|
||||||
|
}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
// WebSocket connections
|
||||||
|
const timerSocket = new WebSocket(`${window.location.protocol === 'https:' ? 'wss:' : 'ws:'}//${window.location.host}/ws/timer`);
|
||||||
|
const adminSocket = new WebSocket(`${window.location.protocol === 'https:' ? 'wss:' : 'ws:'}//${window.location.host}/ws/admin`);
|
||||||
|
|
||||||
|
const timerDisplay = document.getElementById('timer');
|
||||||
|
const statusIndicator = document.getElementById('status-indicator');
|
||||||
|
const gateStatus = document.getElementById('gate-status');
|
||||||
|
const gateStatusText = document.getElementById('gate-status-text');
|
||||||
|
|
||||||
|
// Timer socket handling
|
||||||
|
timerSocket.onmessage = function(event) {
|
||||||
|
const data = JSON.parse(event.data);
|
||||||
|
|
||||||
|
if (data.type === 'time') {
|
||||||
|
timerDisplay.textContent = data.time.toFixed(3);
|
||||||
|
} 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
|
||||||
|
gateStatus.className = 'alert ';
|
||||||
|
if (data.status === 'Open') {
|
||||||
|
gateStatus.className += 'alert-danger';
|
||||||
|
} else if (data.status === 'Closed') {
|
||||||
|
gateStatus.className += 'alert-success';
|
||||||
|
} else {
|
||||||
|
gateStatus.className += 'alert-secondary';
|
||||||
|
}
|
||||||
|
} else if (data.type === 'lane-time') {
|
||||||
|
// Update lane time display
|
||||||
|
const laneTimeElement = document.getElementById(`lane-${data.lane}-time`);
|
||||||
|
if (laneTimeElement) {
|
||||||
|
laneTimeElement.textContent = data.time.toFixed(3);
|
||||||
|
}
|
||||||
|
} else if (data.type === 'lane-position') {
|
||||||
|
// Update lane position display
|
||||||
|
const lanePositionElement = document.getElementById(`lane-${data.lane}-position`);
|
||||||
|
if (lanePositionElement) {
|
||||||
|
lanePositionElement.textContent = data.position;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Admin socket handling
|
||||||
|
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 resetTimer() {
|
||||||
|
fetch('/api/timer/reset', { method: 'POST' })
|
||||||
|
.then(response => {
|
||||||
|
if (!response.ok) {
|
||||||
|
console.error('Failed to reset timer');
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
console.error('Error:', error);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Function to force end the current heat
|
||||||
|
function forceEndHeat() {
|
||||||
|
fetch('/api/timer/force-end', { method: 'POST' })
|
||||||
|
.then(response => {
|
||||||
|
if (!response.ok) {
|
||||||
|
console.error('Failed to force end heat');
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
console.error('Error:', error);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Function to save heat results
|
||||||
|
function saveHeatResults() {
|
||||||
|
// Get lane times and positions from the UI
|
||||||
|
const lanes = [1, 2, 3, 4];
|
||||||
|
const results = {
|
||||||
|
group_id: parseInt(document.getElementById('group-select').value),
|
||||||
|
heat_number: parseInt('{ strconv.Itoa(currentHeatNum) }'),
|
||||||
|
};
|
||||||
|
|
||||||
|
lanes.forEach(lane => {
|
||||||
|
const timeElement = document.getElementById(`lane-${lane}-time`);
|
||||||
|
const positionElement = document.getElementById(`lane-${lane}-position`);
|
||||||
|
|
||||||
|
if (timeElement && positionElement) {
|
||||||
|
const timeText = timeElement.textContent.trim();
|
||||||
|
const positionText = positionElement.textContent.trim();
|
||||||
|
|
||||||
|
results[`lane${lane}_time`] = timeText !== '--.-' ? parseFloat(timeText) : 0;
|
||||||
|
results[`lane${lane}_position`] = positionText !== '-' ? parseInt(positionText) : 0;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Save results to the server
|
||||||
|
fetch('/api/race/save-result', {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
},
|
||||||
|
body: JSON.stringify(results),
|
||||||
|
})
|
||||||
|
.then(response => {
|
||||||
|
if (!response.ok) {
|
||||||
|
console.error('Failed to save heat results');
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
console.error('Error:', error);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Helper template for displaying current heat in the management view
|
||||||
|
templ raceManageCurrentHeat(heats []models.Heat, racers []models.Racer, currentHeatNum int, results []models.HeatResult) {
|
||||||
|
{{
|
||||||
|
// Find current heat
|
||||||
|
var currentHeat models.Heat
|
||||||
|
for _, heat := range heats {
|
||||||
|
if heat.HeatNum == currentHeatNum {
|
||||||
|
currentHeat = heat
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find heat result if available
|
||||||
|
var currentResult *models.HeatResult
|
||||||
|
for _, result := range results {
|
||||||
|
if result.HeatNumber == currentHeatNum {
|
||||||
|
currentResult = &result
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
|
||||||
|
<div class="table-responsive">
|
||||||
|
<table class="table table-bordered">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Lane</th>
|
||||||
|
<th>Racer</th>
|
||||||
|
<th>Car #</th>
|
||||||
|
<th>Time</th>
|
||||||
|
<th>Position</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
if currentHeat.Lane1ID != nil {
|
||||||
|
@raceManageLaneRow(1, *currentHeat.Lane1ID, racers, currentResult)
|
||||||
|
}
|
||||||
|
if currentHeat.Lane2ID != nil {
|
||||||
|
@raceManageLaneRow(2, *currentHeat.Lane2ID, racers, currentResult)
|
||||||
|
}
|
||||||
|
if currentHeat.Lane3ID != nil {
|
||||||
|
@raceManageLaneRow(3, *currentHeat.Lane3ID, racers, currentResult)
|
||||||
|
}
|
||||||
|
if currentHeat.Lane4ID != nil {
|
||||||
|
@raceManageLaneRow(4, *currentHeat.Lane4ID, racers, currentResult)
|
||||||
|
}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
|
||||||
|
// Helper template for displaying a lane row in the management view
|
||||||
|
templ raceManageLaneRow(lane int, racerID int64, racers []models.Racer, result *models.HeatResult) {
|
||||||
|
{{
|
||||||
|
// Find racer
|
||||||
|
var racer models.Racer
|
||||||
|
for _, r := range racers {
|
||||||
|
if r.ID == racerID {
|
||||||
|
racer = r
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get time and position from result if available
|
||||||
|
var time float64
|
||||||
|
var position int
|
||||||
|
var hasResult bool
|
||||||
|
|
||||||
|
if result != nil {
|
||||||
|
hasResult = true
|
||||||
|
switch lane {
|
||||||
|
case 1:
|
||||||
|
time = result.Lane1Time
|
||||||
|
position = result.Lane1Position
|
||||||
|
case 2:
|
||||||
|
time = result.Lane2Time
|
||||||
|
position = result.Lane2Position
|
||||||
|
case 3:
|
||||||
|
time = result.Lane3Time
|
||||||
|
position = result.Lane3Position
|
||||||
|
case 4:
|
||||||
|
time = result.Lane4Time
|
||||||
|
position = result.Lane4Position
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
|
||||||
|
<tr>
|
||||||
|
<td>{ strconv.Itoa(lane) }</td>
|
||||||
|
<td>{ racer.FirstName } { racer.LastName }</td>
|
||||||
|
<td>{ racer.CarNumber }</td>
|
||||||
|
<td id={ fmt.Sprintf("lane-%d-time", lane) }>
|
||||||
|
if hasResult {
|
||||||
|
{ fmt.Sprintf("%.3f", time) }
|
||||||
|
} else {
|
||||||
|
--.-
|
||||||
|
}
|
||||||
|
</td>
|
||||||
|
<td id={ fmt.Sprintf("lane-%d-position", lane) }>
|
||||||
|
if hasResult {
|
||||||
|
{ strconv.Itoa(position) }
|
||||||
|
} else {
|
||||||
|
-
|
||||||
|
}
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
}
|
||||||
|
|
||||||
|
// Helper template for displaying next heat in the management view
|
||||||
|
templ raceManageNextHeat(heats []models.Heat, racers []models.Racer, heatNum int) {
|
||||||
|
{{
|
||||||
|
// Find the heat
|
||||||
|
var nextHeat models.Heat
|
||||||
|
for _, heat := range heats {
|
||||||
|
if heat.HeatNum == heatNum {
|
||||||
|
nextHeat = heat
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
|
||||||
|
<div class="table-responsive">
|
||||||
|
<table class="table table-striped table-sm">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Lane</th>
|
||||||
|
<th>Racer</th>
|
||||||
|
<th>Car #</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
if nextHeat.Lane1ID != nil {
|
||||||
|
@raceManageNextHeatRow(1, *nextHeat.Lane1ID, racers)
|
||||||
|
}
|
||||||
|
if nextHeat.Lane2ID != nil {
|
||||||
|
@raceManageNextHeatRow(2, *nextHeat.Lane2ID, racers)
|
||||||
|
}
|
||||||
|
if nextHeat.Lane3ID != nil {
|
||||||
|
@raceManageNextHeatRow(3, *nextHeat.Lane3ID, racers)
|
||||||
|
}
|
||||||
|
if nextHeat.Lane4ID != nil {
|
||||||
|
@raceManageNextHeatRow(4, *nextHeat.Lane4ID, racers)
|
||||||
|
}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
|
||||||
|
// Helper template for displaying a row in the next heat preview
|
||||||
|
templ raceManageNextHeatRow(lane int, racerID int64, racers []models.Racer) {
|
||||||
|
{{
|
||||||
|
// Find racer
|
||||||
|
var racer models.Racer
|
||||||
|
for _, r := range racers {
|
||||||
|
if r.ID == racerID {
|
||||||
|
racer = r
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
|
||||||
|
<tr>
|
||||||
|
<td>{ strconv.Itoa(lane) }</td>
|
||||||
|
<td>{ racer.FirstName } { racer.LastName }</td>
|
||||||
|
<td>{ racer.CarNumber }</td>
|
||||||
|
</tr>
|
||||||
|
}
|
||||||
File diff suppressed because one or more lines are too long
@ -0,0 +1,297 @@
|
|||||||
|
package templates
|
||||||
|
|
||||||
|
import (
|
||||||
|
"track-gopher/models"
|
||||||
|
"fmt"
|
||||||
|
"strconv"
|
||||||
|
)
|
||||||
|
|
||||||
|
// RacePublic renders the public race view
|
||||||
|
templ RacePublic(currentGroup models.Group, heats []models.Heat, racers []models.Racer, currentHeatNum int, results []models.HeatResult) {
|
||||||
|
@Layout("Race - " + currentGroup.Name) {
|
||||||
|
<div class="container-fluid mt-3">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-12">
|
||||||
|
<div class="card mb-4">
|
||||||
|
<div class="card-header bg-primary text-white">
|
||||||
|
<h2 class="mb-0 text-center">{ currentGroup.Name } - Heat { strconv.Itoa(currentHeatNum) } of { strconv.Itoa(len(heats)) }</h2>
|
||||||
|
</div>
|
||||||
|
<div class="card-body">
|
||||||
|
<div id="current-heat" class="mb-4">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-12">
|
||||||
|
<div class="timer-display text-center mb-4">
|
||||||
|
<div id="timer" class="display-1 fw-bold">0.000</div>
|
||||||
|
<div id="status-indicator" class="badge bg-secondary">Ready</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="lanes-container">
|
||||||
|
@raceCurrentHeatLanes(heats, racers, currentHeatNum, results)
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-md-6">
|
||||||
|
<div class="card mb-4">
|
||||||
|
<div class="card-header bg-info text-white">
|
||||||
|
<h3 class="mb-0">Next Heat</h3>
|
||||||
|
</div>
|
||||||
|
<div class="card-body">
|
||||||
|
if currentHeatNum < len(heats) {
|
||||||
|
@raceNextHeatPreview(heats, racers, currentHeatNum+1)
|
||||||
|
} else {
|
||||||
|
<div class="alert alert-info">No more heats in this group</div>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-6">
|
||||||
|
<div class="card mb-4">
|
||||||
|
<div class="card-header bg-info text-white">
|
||||||
|
<h3 class="mb-0">Upcoming Heat</h3>
|
||||||
|
</div>
|
||||||
|
<div class="card-body">
|
||||||
|
if currentHeatNum+1 < len(heats) {
|
||||||
|
@raceNextHeatPreview(heats, racers, currentHeatNum+2)
|
||||||
|
} else {
|
||||||
|
<div class="alert alert-info">No more heats in this group</div>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
// WebSocket connection for timer updates
|
||||||
|
const timerSocket = new WebSocket(`${window.location.protocol === 'https:' ? 'wss:' : 'ws:'}//${window.location.host}/ws/timer`);
|
||||||
|
const timerDisplay = document.getElementById('timer');
|
||||||
|
const statusIndicator = document.getElementById('status-indicator');
|
||||||
|
|
||||||
|
timerSocket.onmessage = function(event) {
|
||||||
|
const data = JSON.parse(event.data);
|
||||||
|
|
||||||
|
if (data.type === 'time') {
|
||||||
|
timerDisplay.textContent = data.time.toFixed(3);
|
||||||
|
} else if (data.type === 'status') {
|
||||||
|
statusIndicator.textContent = data.status;
|
||||||
|
|
||||||
|
// Update status indicator color
|
||||||
|
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) {
|
||||||
|
laneTimeElement.textContent = data.time.toFixed(3);
|
||||||
|
}
|
||||||
|
} else if (data.type === 'lane-position') {
|
||||||
|
// Update lane position display
|
||||||
|
const lanePositionElement = document.getElementById(`lane-${data.lane}-position`);
|
||||||
|
if (lanePositionElement) {
|
||||||
|
lanePositionElement.textContent = data.position;
|
||||||
|
}
|
||||||
|
} else if (data.type === 'reload') {
|
||||||
|
// Reload the page when heat changes
|
||||||
|
window.location.reload();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Auto-refresh the page every 30 seconds to keep data current
|
||||||
|
setTimeout(() => {
|
||||||
|
window.location.reload();
|
||||||
|
}, 30000);
|
||||||
|
</script>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Helper template for displaying current heat lanes
|
||||||
|
templ raceCurrentHeatLanes(heats []models.Heat, racers []models.Racer, currentHeatNum int, results []models.HeatResult) {
|
||||||
|
{{
|
||||||
|
// Find current heat
|
||||||
|
var currentHeat models.Heat
|
||||||
|
for _, heat := range heats {
|
||||||
|
if heat.HeatNum == currentHeatNum {
|
||||||
|
currentHeat = heat
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find heat result if available
|
||||||
|
var currentResult *models.HeatResult
|
||||||
|
for _, result := range results {
|
||||||
|
if result.HeatNumber == currentHeatNum {
|
||||||
|
currentResult = &result
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
|
||||||
|
<div class="row row-cols-1 row-cols-md-4 g-4">
|
||||||
|
if currentHeat.Lane1ID != nil {
|
||||||
|
@raceLaneCard(1, *currentHeat.Lane1ID, racers, currentResult)
|
||||||
|
}
|
||||||
|
if currentHeat.Lane2ID != nil {
|
||||||
|
@raceLaneCard(2, *currentHeat.Lane2ID, racers, currentResult)
|
||||||
|
}
|
||||||
|
if currentHeat.Lane3ID != nil {
|
||||||
|
@raceLaneCard(3, *currentHeat.Lane3ID, racers, currentResult)
|
||||||
|
}
|
||||||
|
if currentHeat.Lane4ID != nil {
|
||||||
|
@raceLaneCard(4, *currentHeat.Lane4ID, racers, currentResult)
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
|
||||||
|
// Helper template for displaying a lane card
|
||||||
|
templ raceLaneCard(lane int, racerID int64, racers []models.Racer, result *models.HeatResult) {
|
||||||
|
{{
|
||||||
|
// Find racer
|
||||||
|
var racer models.Racer
|
||||||
|
for _, r := range racers {
|
||||||
|
if r.ID == racerID {
|
||||||
|
racer = r
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get time and position from result if available
|
||||||
|
var time float64
|
||||||
|
var position int
|
||||||
|
var hasResult bool
|
||||||
|
|
||||||
|
if result != nil {
|
||||||
|
hasResult = true
|
||||||
|
switch lane {
|
||||||
|
case 1:
|
||||||
|
time = result.Lane1Time
|
||||||
|
position = result.Lane1Position
|
||||||
|
case 2:
|
||||||
|
time = result.Lane2Time
|
||||||
|
position = result.Lane2Position
|
||||||
|
case 3:
|
||||||
|
time = result.Lane3Time
|
||||||
|
position = result.Lane3Position
|
||||||
|
case 4:
|
||||||
|
time = result.Lane4Time
|
||||||
|
position = result.Lane4Position
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
|
||||||
|
<div class="col">
|
||||||
|
<div class="card h-100 lane-card">
|
||||||
|
<div class="card-header bg-primary text-white">
|
||||||
|
<h4 class="mb-0">Lane { strconv.Itoa(lane) }</h4>
|
||||||
|
</div>
|
||||||
|
<div class="card-body">
|
||||||
|
<h5 class="card-title">{ racer.FirstName } { racer.LastName }</h5>
|
||||||
|
<p class="card-text">
|
||||||
|
<strong>Car #:</strong> { racer.CarNumber }<br/>
|
||||||
|
<strong>Weight:</strong> { fmt.Sprintf("%.1f oz", racer.CarWeight) }
|
||||||
|
</p>
|
||||||
|
<div class="result-area">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-6">
|
||||||
|
<div class="text-center">
|
||||||
|
<h6>Time</h6>
|
||||||
|
<div id={ fmt.Sprintf("lane-%d-time", lane) } class="display-6">
|
||||||
|
if hasResult {
|
||||||
|
{ fmt.Sprintf("%.3f", time) }
|
||||||
|
} else {
|
||||||
|
--.-
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-6">
|
||||||
|
<div class="text-center">
|
||||||
|
<h6>Position</h6>
|
||||||
|
<div id={ fmt.Sprintf("lane-%d-position", lane) } class="display-6">
|
||||||
|
if hasResult {
|
||||||
|
{ strconv.Itoa(position) }
|
||||||
|
} else {
|
||||||
|
-
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
|
||||||
|
// Helper template for displaying next heat preview
|
||||||
|
templ raceNextHeatPreview(heats []models.Heat, racers []models.Racer, heatNum int) {
|
||||||
|
{{
|
||||||
|
// Find the heat
|
||||||
|
var nextHeat models.Heat
|
||||||
|
for _, heat := range heats {
|
||||||
|
if heat.HeatNum == heatNum {
|
||||||
|
nextHeat = heat
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
|
||||||
|
<h4 class="mb-3">Heat { strconv.Itoa(heatNum) }</h4>
|
||||||
|
<div class="table-responsive">
|
||||||
|
<table class="table table-striped">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Lane</th>
|
||||||
|
<th>Racer</th>
|
||||||
|
<th>Car #</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
if nextHeat.Lane1ID != nil {
|
||||||
|
@raceNextHeatRow(1, *nextHeat.Lane1ID, racers)
|
||||||
|
}
|
||||||
|
if nextHeat.Lane2ID != nil {
|
||||||
|
@raceNextHeatRow(2, *nextHeat.Lane2ID, racers)
|
||||||
|
}
|
||||||
|
if nextHeat.Lane3ID != nil {
|
||||||
|
@raceNextHeatRow(3, *nextHeat.Lane3ID, racers)
|
||||||
|
}
|
||||||
|
if nextHeat.Lane4ID != nil {
|
||||||
|
@raceNextHeatRow(4, *nextHeat.Lane4ID, racers)
|
||||||
|
}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
|
||||||
|
// Helper template for displaying a row in the next heat preview
|
||||||
|
templ raceNextHeatRow(lane int, racerID int64, racers []models.Racer) {
|
||||||
|
{{
|
||||||
|
// Find racer
|
||||||
|
var racer models.Racer
|
||||||
|
for _, r := range racers {
|
||||||
|
if r.ID == racerID {
|
||||||
|
racer = r
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
|
||||||
|
<tr>
|
||||||
|
<td>{ strconv.Itoa(lane) }</td>
|
||||||
|
<td>{ racer.FirstName } { racer.LastName }</td>
|
||||||
|
<td>{ racer.CarNumber }</td>
|
||||||
|
</tr>
|
||||||
|
}
|
||||||
@ -0,0 +1,582 @@
|
|||||||
|
// Code generated by templ - DO NOT EDIT.
|
||||||
|
|
||||||
|
// templ: version: v0.3.833
|
||||||
|
package templates
|
||||||
|
|
||||||
|
//lint:file-ignore SA4006 This context is only used if a nested component is present.
|
||||||
|
|
||||||
|
import "github.com/a-h/templ"
|
||||||
|
import templruntime "github.com/a-h/templ/runtime"
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strconv"
|
||||||
|
"track-gopher/models"
|
||||||
|
)
|
||||||
|
|
||||||
|
// RacePublic renders the public race view
|
||||||
|
func RacePublic(currentGroup models.Group, heats []models.Heat, racers []models.Racer, currentHeatNum int, results []models.HeatResult) templ.Component {
|
||||||
|
return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
|
||||||
|
templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
|
||||||
|
if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil {
|
||||||
|
return templ_7745c5c3_CtxErr
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
|
||||||
|
if !templ_7745c5c3_IsBuffer {
|
||||||
|
defer func() {
|
||||||
|
templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
|
||||||
|
if templ_7745c5c3_Err == nil {
|
||||||
|
templ_7745c5c3_Err = templ_7745c5c3_BufErr
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
ctx = templ.InitializeContext(ctx)
|
||||||
|
templ_7745c5c3_Var1 := templ.GetChildren(ctx)
|
||||||
|
if templ_7745c5c3_Var1 == nil {
|
||||||
|
templ_7745c5c3_Var1 = templ.NopComponent
|
||||||
|
}
|
||||||
|
ctx = templ.ClearChildren(ctx)
|
||||||
|
templ_7745c5c3_Var2 := templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
|
||||||
|
templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
|
||||||
|
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
|
||||||
|
if !templ_7745c5c3_IsBuffer {
|
||||||
|
defer func() {
|
||||||
|
templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
|
||||||
|
if templ_7745c5c3_Err == nil {
|
||||||
|
templ_7745c5c3_Err = templ_7745c5c3_BufErr
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
ctx = templ.InitializeContext(ctx)
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 1, "<div class=\"container-fluid mt-3\"><div class=\"row\"><div class=\"col-12\"><div class=\"card mb-4\"><div class=\"card-header bg-primary text-white\"><h2 class=\"mb-0 text-center\">")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
var templ_7745c5c3_Var3 string
|
||||||
|
templ_7745c5c3_Var3, templ_7745c5c3_Err = templ.JoinStringErrs(currentGroup.Name)
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `web/templates/race_public.templ`, Line: 17, Col: 76}
|
||||||
|
}
|
||||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var3))
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 2, " - Heat ")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
var templ_7745c5c3_Var4 string
|
||||||
|
templ_7745c5c3_Var4, templ_7745c5c3_Err = templ.JoinStringErrs(strconv.Itoa(currentHeatNum))
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `web/templates/race_public.templ`, Line: 17, Col: 116}
|
||||||
|
}
|
||||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var4))
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 3, " of ")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
var templ_7745c5c3_Var5 string
|
||||||
|
templ_7745c5c3_Var5, templ_7745c5c3_Err = templ.JoinStringErrs(strconv.Itoa(len(heats)))
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `web/templates/race_public.templ`, Line: 17, Col: 148}
|
||||||
|
}
|
||||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var5))
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 4, "</h2></div><div class=\"card-body\"><div id=\"current-heat\" class=\"mb-4\"><div class=\"row\"><div class=\"col-12\"><div class=\"timer-display text-center mb-4\"><div id=\"timer\" class=\"display-1 fw-bold\">0.000</div><div id=\"status-indicator\" class=\"badge bg-secondary\">Ready</div></div><div class=\"lanes-container\">")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = raceCurrentHeatLanes(heats, racers, currentHeatNum, results).Render(ctx, templ_7745c5c3_Buffer)
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 5, "</div></div></div></div></div></div></div></div><div class=\"row\"><div class=\"col-md-6\"><div class=\"card mb-4\"><div class=\"card-header bg-info text-white\"><h3 class=\"mb-0\">Next Heat</h3></div><div class=\"card-body\">")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
if currentHeatNum < len(heats) {
|
||||||
|
templ_7745c5c3_Err = raceNextHeatPreview(heats, racers, currentHeatNum+1).Render(ctx, templ_7745c5c3_Buffer)
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 6, "<div class=\"alert alert-info\">No more heats in this group</div>")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 7, "</div></div></div><div class=\"col-md-6\"><div class=\"card mb-4\"><div class=\"card-header bg-info text-white\"><h3 class=\"mb-0\">Upcoming Heat</h3></div><div class=\"card-body\">")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
if currentHeatNum+1 < len(heats) {
|
||||||
|
templ_7745c5c3_Err = raceNextHeatPreview(heats, racers, currentHeatNum+2).Render(ctx, templ_7745c5c3_Buffer)
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 8, "<div class=\"alert alert-info\">No more heats in this group</div>")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 9, "</div></div></div></div></div><script>\r\n // WebSocket connection for timer updates\r\n const timerSocket = new WebSocket(`${window.location.protocol === 'https:' ? 'wss:' : 'ws:'}//${window.location.host}/ws/timer`);\r\n const timerDisplay = document.getElementById('timer');\r\n const statusIndicator = document.getElementById('status-indicator');\r\n \r\n timerSocket.onmessage = function(event) {\r\n const data = JSON.parse(event.data);\r\n \r\n if (data.type === 'time') {\r\n timerDisplay.textContent = data.time.toFixed(3);\r\n } else if (data.type === 'status') {\r\n statusIndicator.textContent = data.status;\r\n \r\n // Update status indicator color\r\n statusIndicator.className = 'badge ';\r\n if (data.status === 'Ready') {\r\n statusIndicator.className += 'bg-secondary';\r\n } else if (data.status === 'Running') {\r\n statusIndicator.className += 'bg-success';\r\n } else if (data.status === 'Finished') {\r\n statusIndicator.className += 'bg-primary';\r\n }\r\n } else if (data.type === 'lane-time') {\r\n // Update lane time display\r\n const laneTimeElement = document.getElementById(`lane-${data.lane}-time`);\r\n if (laneTimeElement) {\r\n laneTimeElement.textContent = data.time.toFixed(3);\r\n }\r\n } else if (data.type === 'lane-position') {\r\n // Update lane position display\r\n const lanePositionElement = document.getElementById(`lane-${data.lane}-position`);\r\n if (lanePositionElement) {\r\n lanePositionElement.textContent = data.position;\r\n }\r\n } else if (data.type === 'reload') {\r\n // Reload the page when heat changes\r\n window.location.reload();\r\n }\r\n };\r\n \r\n // Auto-refresh the page every 30 seconds to keep data current\r\n setTimeout(() => {\r\n window.location.reload();\r\n }, 30000);\r\n </script>")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
templ_7745c5c3_Err = Layout("Race - "+currentGroup.Name).Render(templ.WithChildren(ctx, templ_7745c5c3_Var2), templ_7745c5c3_Buffer)
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Helper template for displaying current heat lanes
|
||||||
|
func raceCurrentHeatLanes(heats []models.Heat, racers []models.Racer, currentHeatNum int, results []models.HeatResult) templ.Component {
|
||||||
|
return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
|
||||||
|
templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
|
||||||
|
if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil {
|
||||||
|
return templ_7745c5c3_CtxErr
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
|
||||||
|
if !templ_7745c5c3_IsBuffer {
|
||||||
|
defer func() {
|
||||||
|
templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
|
||||||
|
if templ_7745c5c3_Err == nil {
|
||||||
|
templ_7745c5c3_Err = templ_7745c5c3_BufErr
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
ctx = templ.InitializeContext(ctx)
|
||||||
|
templ_7745c5c3_Var6 := templ.GetChildren(ctx)
|
||||||
|
if templ_7745c5c3_Var6 == nil {
|
||||||
|
templ_7745c5c3_Var6 = templ.NopComponent
|
||||||
|
}
|
||||||
|
ctx = templ.ClearChildren(ctx)
|
||||||
|
|
||||||
|
// Find current heat
|
||||||
|
var currentHeat models.Heat
|
||||||
|
for _, heat := range heats {
|
||||||
|
if heat.HeatNum == currentHeatNum {
|
||||||
|
currentHeat = heat
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find heat result if available
|
||||||
|
var currentResult *models.HeatResult
|
||||||
|
for _, result := range results {
|
||||||
|
if result.HeatNumber == currentHeatNum {
|
||||||
|
currentResult = &result
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 10, "<div class=\"row row-cols-1 row-cols-md-4 g-4\">")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
if currentHeat.Lane1ID != nil {
|
||||||
|
templ_7745c5c3_Err = raceLaneCard(1, *currentHeat.Lane1ID, racers, currentResult).Render(ctx, templ_7745c5c3_Buffer)
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if currentHeat.Lane2ID != nil {
|
||||||
|
templ_7745c5c3_Err = raceLaneCard(2, *currentHeat.Lane2ID, racers, currentResult).Render(ctx, templ_7745c5c3_Buffer)
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if currentHeat.Lane3ID != nil {
|
||||||
|
templ_7745c5c3_Err = raceLaneCard(3, *currentHeat.Lane3ID, racers, currentResult).Render(ctx, templ_7745c5c3_Buffer)
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if currentHeat.Lane4ID != nil {
|
||||||
|
templ_7745c5c3_Err = raceLaneCard(4, *currentHeat.Lane4ID, racers, currentResult).Render(ctx, templ_7745c5c3_Buffer)
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 11, "</div>")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Helper template for displaying a lane card
|
||||||
|
func raceLaneCard(lane int, racerID int64, racers []models.Racer, result *models.HeatResult) templ.Component {
|
||||||
|
return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
|
||||||
|
templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
|
||||||
|
if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil {
|
||||||
|
return templ_7745c5c3_CtxErr
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
|
||||||
|
if !templ_7745c5c3_IsBuffer {
|
||||||
|
defer func() {
|
||||||
|
templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
|
||||||
|
if templ_7745c5c3_Err == nil {
|
||||||
|
templ_7745c5c3_Err = templ_7745c5c3_BufErr
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
ctx = templ.InitializeContext(ctx)
|
||||||
|
templ_7745c5c3_Var7 := templ.GetChildren(ctx)
|
||||||
|
if templ_7745c5c3_Var7 == nil {
|
||||||
|
templ_7745c5c3_Var7 = templ.NopComponent
|
||||||
|
}
|
||||||
|
ctx = templ.ClearChildren(ctx)
|
||||||
|
|
||||||
|
// Find racer
|
||||||
|
var racer models.Racer
|
||||||
|
for _, r := range racers {
|
||||||
|
if r.ID == racerID {
|
||||||
|
racer = r
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get time and position from result if available
|
||||||
|
var time float64
|
||||||
|
var position int
|
||||||
|
var hasResult bool
|
||||||
|
|
||||||
|
if result != nil {
|
||||||
|
hasResult = true
|
||||||
|
switch lane {
|
||||||
|
case 1:
|
||||||
|
time = result.Lane1Time
|
||||||
|
position = result.Lane1Position
|
||||||
|
case 2:
|
||||||
|
time = result.Lane2Time
|
||||||
|
position = result.Lane2Position
|
||||||
|
case 3:
|
||||||
|
time = result.Lane3Time
|
||||||
|
position = result.Lane3Position
|
||||||
|
case 4:
|
||||||
|
time = result.Lane4Time
|
||||||
|
position = result.Lane4Position
|
||||||
|
}
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 12, "<div class=\"col\"><div class=\"card h-100 lane-card\"><div class=\"card-header bg-primary text-white\"><h4 class=\"mb-0\">Lane ")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
var templ_7745c5c3_Var8 string
|
||||||
|
templ_7745c5c3_Var8, templ_7745c5c3_Err = templ.JoinStringErrs(strconv.Itoa(lane))
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `web/templates/race_public.templ`, Line: 197, Col: 58}
|
||||||
|
}
|
||||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var8))
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 13, "</h4></div><div class=\"card-body\"><h5 class=\"card-title\">")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
var templ_7745c5c3_Var9 string
|
||||||
|
templ_7745c5c3_Var9, templ_7745c5c3_Err = templ.JoinStringErrs(racer.FirstName)
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `web/templates/race_public.templ`, Line: 200, Col: 56}
|
||||||
|
}
|
||||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var9))
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 14, " ")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
var templ_7745c5c3_Var10 string
|
||||||
|
templ_7745c5c3_Var10, templ_7745c5c3_Err = templ.JoinStringErrs(racer.LastName)
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `web/templates/race_public.templ`, Line: 200, Col: 75}
|
||||||
|
}
|
||||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var10))
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 15, "</h5><p class=\"card-text\"><strong>Car #:</strong> ")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
var templ_7745c5c3_Var11 string
|
||||||
|
templ_7745c5c3_Var11, templ_7745c5c3_Err = templ.JoinStringErrs(racer.CarNumber)
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `web/templates/race_public.templ`, Line: 202, Col: 61}
|
||||||
|
}
|
||||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var11))
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 16, "<br><strong>Weight:</strong> ")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
var templ_7745c5c3_Var12 string
|
||||||
|
templ_7745c5c3_Var12, templ_7745c5c3_Err = templ.JoinStringErrs(fmt.Sprintf("%.1f oz", racer.CarWeight))
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `web/templates/race_public.templ`, Line: 203, Col: 86}
|
||||||
|
}
|
||||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var12))
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 17, "</p><div class=\"result-area\"><div class=\"row\"><div class=\"col-6\"><div class=\"text-center\"><h6>Time</h6><div id=\"")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
var templ_7745c5c3_Var13 string
|
||||||
|
templ_7745c5c3_Var13, templ_7745c5c3_Err = templ.JoinStringErrs(fmt.Sprintf("lane-%d-time", lane))
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `web/templates/race_public.templ`, Line: 210, Col: 75}
|
||||||
|
}
|
||||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var13))
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 18, "\" class=\"display-6\">")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
if hasResult {
|
||||||
|
var templ_7745c5c3_Var14 string
|
||||||
|
templ_7745c5c3_Var14, templ_7745c5c3_Err = templ.JoinStringErrs(fmt.Sprintf("%.3f", time))
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `web/templates/race_public.templ`, Line: 212, Col: 67}
|
||||||
|
}
|
||||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var14))
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 19, "--.-")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 20, "</div></div></div><div class=\"col-6\"><div class=\"text-center\"><h6>Position</h6><div id=\"")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
var templ_7745c5c3_Var15 string
|
||||||
|
templ_7745c5c3_Var15, templ_7745c5c3_Err = templ.JoinStringErrs(fmt.Sprintf("lane-%d-position", lane))
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `web/templates/race_public.templ`, Line: 222, Col: 79}
|
||||||
|
}
|
||||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var15))
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 21, "\" class=\"display-6\">")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
if hasResult {
|
||||||
|
var templ_7745c5c3_Var16 string
|
||||||
|
templ_7745c5c3_Var16, templ_7745c5c3_Err = templ.JoinStringErrs(strconv.Itoa(position))
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `web/templates/race_public.templ`, Line: 224, Col: 64}
|
||||||
|
}
|
||||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var16))
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 22, "-")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 23, "</div></div></div></div></div></div></div></div>")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Helper template for displaying next heat preview
|
||||||
|
func raceNextHeatPreview(heats []models.Heat, racers []models.Racer, heatNum int) templ.Component {
|
||||||
|
return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
|
||||||
|
templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
|
||||||
|
if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil {
|
||||||
|
return templ_7745c5c3_CtxErr
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
|
||||||
|
if !templ_7745c5c3_IsBuffer {
|
||||||
|
defer func() {
|
||||||
|
templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
|
||||||
|
if templ_7745c5c3_Err == nil {
|
||||||
|
templ_7745c5c3_Err = templ_7745c5c3_BufErr
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
ctx = templ.InitializeContext(ctx)
|
||||||
|
templ_7745c5c3_Var17 := templ.GetChildren(ctx)
|
||||||
|
if templ_7745c5c3_Var17 == nil {
|
||||||
|
templ_7745c5c3_Var17 = templ.NopComponent
|
||||||
|
}
|
||||||
|
ctx = templ.ClearChildren(ctx)
|
||||||
|
|
||||||
|
// Find the heat
|
||||||
|
var nextHeat models.Heat
|
||||||
|
for _, heat := range heats {
|
||||||
|
if heat.HeatNum == heatNum {
|
||||||
|
nextHeat = heat
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 24, "<h4 class=\"mb-3\">Heat ")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
var templ_7745c5c3_Var18 string
|
||||||
|
templ_7745c5c3_Var18, templ_7745c5c3_Err = templ.JoinStringErrs(strconv.Itoa(heatNum))
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `web/templates/race_public.templ`, Line: 251, Col: 49}
|
||||||
|
}
|
||||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var18))
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 25, "</h4><div class=\"table-responsive\"><table class=\"table table-striped\"><thead><tr><th>Lane</th><th>Racer</th><th>Car #</th></tr></thead> <tbody>")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
if nextHeat.Lane1ID != nil {
|
||||||
|
templ_7745c5c3_Err = raceNextHeatRow(1, *nextHeat.Lane1ID, racers).Render(ctx, templ_7745c5c3_Buffer)
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if nextHeat.Lane2ID != nil {
|
||||||
|
templ_7745c5c3_Err = raceNextHeatRow(2, *nextHeat.Lane2ID, racers).Render(ctx, templ_7745c5c3_Buffer)
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if nextHeat.Lane3ID != nil {
|
||||||
|
templ_7745c5c3_Err = raceNextHeatRow(3, *nextHeat.Lane3ID, racers).Render(ctx, templ_7745c5c3_Buffer)
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if nextHeat.Lane4ID != nil {
|
||||||
|
templ_7745c5c3_Err = raceNextHeatRow(4, *nextHeat.Lane4ID, racers).Render(ctx, templ_7745c5c3_Buffer)
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 26, "</tbody></table></div>")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Helper template for displaying a row in the next heat preview
|
||||||
|
func raceNextHeatRow(lane int, racerID int64, racers []models.Racer) templ.Component {
|
||||||
|
return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
|
||||||
|
templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
|
||||||
|
if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil {
|
||||||
|
return templ_7745c5c3_CtxErr
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
|
||||||
|
if !templ_7745c5c3_IsBuffer {
|
||||||
|
defer func() {
|
||||||
|
templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
|
||||||
|
if templ_7745c5c3_Err == nil {
|
||||||
|
templ_7745c5c3_Err = templ_7745c5c3_BufErr
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
ctx = templ.InitializeContext(ctx)
|
||||||
|
templ_7745c5c3_Var19 := templ.GetChildren(ctx)
|
||||||
|
if templ_7745c5c3_Var19 == nil {
|
||||||
|
templ_7745c5c3_Var19 = templ.NopComponent
|
||||||
|
}
|
||||||
|
ctx = templ.ClearChildren(ctx)
|
||||||
|
|
||||||
|
// Find racer
|
||||||
|
var racer models.Racer
|
||||||
|
for _, r := range racers {
|
||||||
|
if r.ID == racerID {
|
||||||
|
racer = r
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 27, "<tr><td>")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
var templ_7745c5c3_Var20 string
|
||||||
|
templ_7745c5c3_Var20, templ_7745c5c3_Err = templ.JoinStringErrs(strconv.Itoa(lane))
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `web/templates/race_public.templ`, Line: 293, Col: 32}
|
||||||
|
}
|
||||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var20))
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 28, "</td><td>")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
var templ_7745c5c3_Var21 string
|
||||||
|
templ_7745c5c3_Var21, templ_7745c5c3_Err = templ.JoinStringErrs(racer.FirstName)
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `web/templates/race_public.templ`, Line: 294, Col: 29}
|
||||||
|
}
|
||||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var21))
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 29, " ")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
var templ_7745c5c3_Var22 string
|
||||||
|
templ_7745c5c3_Var22, templ_7745c5c3_Err = templ.JoinStringErrs(racer.LastName)
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `web/templates/race_public.templ`, Line: 294, Col: 48}
|
||||||
|
}
|
||||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var22))
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 30, "</td><td>")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
var templ_7745c5c3_Var23 string
|
||||||
|
templ_7745c5c3_Var23, templ_7745c5c3_Err = templ.JoinStringErrs(racer.CarNumber)
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `web/templates/race_public.templ`, Line: 295, Col: 29}
|
||||||
|
}
|
||||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var23))
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 31, "</td></tr>")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ = templruntime.GeneratedTemplate
|
||||||
Loading…
Reference in new issue