Update events

main
DustyP 9 months ago
parent 03e62c48d7
commit 70a55b51bd

@ -44,7 +44,7 @@ const (
// Event represents a race event // Event represents a race event
type Event struct { type Event struct {
Type EventType Type EventType
Event any // Only populated for EventLaneFinish Result *Result // Only populated for EventLaneFinish
} }
// DerbyClock represents the connection to the derby clock device // DerbyClock represents the connection to the derby clock device
@ -206,7 +206,7 @@ func (dc *DerbyClock) readLoop() {
// Send lane finish event // Send lane finish event
dc.eventChan <- Event{ dc.eventChan <- Event{
Type: EventLaneFinish, Type: EventLaneFinish,
Event: result, Result: result,
} }
} }
results = append(results, result) results = append(results, result)

@ -162,10 +162,10 @@ func startTerminalInterface(clock *derby.DerbyClock, events <-chan derby.Event,
fmt.Println("\n🏁 Race started!") fmt.Println("\n🏁 Race started!")
case derby.EventLaneFinish: case derby.EventLaneFinish:
result := event.Event.(derby.Result) result := event.Result
fmt.Printf("🚗 Lane %d finished in place %d with time %.4f seconds\n", fmt.Printf("🚗 Lane %d finished in place %d with time %.4f seconds\n",
result.Lane, result.FinishPlace, result.Time) result.Lane, result.FinishPlace, result.Time)
raceResults = append(raceResults, &result) raceResults = append(raceResults, result)
case derby.EventRaceComplete: case derby.EventRaceComplete:
fmt.Println("\n🏆 Race complete! Final results:") fmt.Println("\n🏆 Race complete! Final results:")

@ -87,3 +87,18 @@ type HeatData struct {
Lane3 *LaneData Lane3 *LaneData
Lane4 *LaneData Lane4 *LaneData
} }
// Event types that can be sent on the event channel
type AdminEventType int
const (
EventHeatChanged AdminEventType = iota
EventGroupChanged
EventHeatRerun
)
// Event represents a race event
type AdminEvent struct {
Type AdminEventType
Event any
}

@ -34,6 +34,8 @@ type Server struct {
events <-chan derby.Event events <-chan derby.Event
clients map[chan string]bool clients map[chan string]bool
clientsMux sync.Mutex clientsMux sync.Mutex
adminclients map[chan string]bool
adminclientsMux sync.Mutex
port int port int
server *http.Server server *http.Server
shutdown chan struct{} shutdown chan struct{}
@ -115,54 +117,49 @@ func (s *Server) routes() {
r.Post("/reset", s.handleReset()) r.Post("/reset", s.handleReset())
r.Post("/force-end", s.handleForceEnd()) r.Post("/force-end", s.handleForceEnd())
r.Get("/events", s.handleEvents()) r.Get("/events", s.handleEvents())
}) r.Get("/validate/car-number", s.handleValidateCarNumber())
r.Route("/groups", func(r chi.Router) {
s.router.Get("/admin", s.handleAdmin())
s.router.Get("/register", s.handleRegister())
s.router.Get("/register/form", s.handleRegisterForm())
s.router.Route("/api/groups", func(r chi.Router) {
r.Post("/", s.handleCreateGroup()) r.Post("/", s.handleCreateGroup())
r.Put("/{id}", s.handleUpdateGroup()) r.Put("/{id}", s.handleUpdateGroup())
r.Delete("/{id}", s.handleDeleteGroup()) r.Delete("/{id}", s.handleDeleteGroup())
}) })
r.Route("/racers", func(r chi.Router) {
s.router.Route("/api/racers", func(r chi.Router) {
r.Post("/", s.handleCreateRacer()) r.Post("/", s.handleCreateRacer())
r.Put("/{id}", s.handleUpdateRacer()) r.Put("/{id}", s.handleUpdateRacer())
r.Delete("/{id}", s.handleDeleteRacer()) r.Delete("/{id}", s.handleDeleteRacer())
}) })
r.Route("/heats", func(r chi.Router) {
r.Post("/generate", s.handleGenerateHeats())
})
r.Route("/api/race", func(r chi.Router) {
r.Get("/current-heat", s.handleCurrentHeat())
r.Post("/next-heat", s.handleNextHeat())
r.Post("/previous-heat", s.handlePreviousHeat())
r.Post("/rerun-heat", s.handleRerunHeat())
r.Post("/set-group", s.handleSetRacingGroup())
})
r.Route("/admin", func(r chi.Router) {
r.Get("/events", s.handleAdminEvents())
})
})
s.router.Get("/admin", s.handleAdmin())
s.router.Get("/register", s.handleRegister())
s.router.Get("/register/form", s.handleRegisterForm())
// Add heats page route // Add heats page route
s.router.Get("/heats", s.handleHeats()) s.router.Get("/heats", s.handleHeats())
s.router.Get("/heats-content", s.handleHeatsContent()) s.router.Get("/heats-content", s.handleHeatsContent())
// Add heats API routes
s.router.Route("/api/heats", func(r chi.Router) {
r.Post("/generate", s.handleGenerateHeats())
})
// Main page // Main page
s.router.Get("/", s.handleIndex()) s.router.Get("/", s.handleIndex())
// Add racers list route // Add racers list route
s.router.Get("/admin/racers/list", s.handleRacersList()) s.router.Get("/admin/racers/list", s.handleRacersList())
// Add validate car number route
s.router.Get("/api/validate/car-number", s.handleValidateCarNumber())
// Add race manager routes // Add race manager routes
s.router.Get("/race", s.handleRacePublic()) s.router.Get("/race", s.handleRacePublic())
s.router.Get("/race/manage", s.handleRaceManage()) s.router.Get("/race/manage", s.handleRaceManage())
// Add race API routes
s.router.Route("/api/race", func(r chi.Router) {
r.Get("/current-heat", s.handleCurrentHeat())
r.Post("/next-heat", s.handleNextHeat())
r.Post("/previous-heat", s.handlePreviousHeat())
r.Post("/rerun-heat", s.handleRerunHeat())
r.Post("/set-group", s.handleSetRacingGroup())
})
} }
// Start starts the web server // Start starts the web server
@ -216,7 +213,7 @@ func (s *Server) forwardEvents() {
return return
} }
// Process the event and send to clients // Process the event and send to clients
s.broadcastEvent(event) s.broadcastRaceEvent(event)
case <-s.shutdown: case <-s.shutdown:
return return
} }
@ -224,37 +221,89 @@ func (s *Server) forwardEvents() {
} }
// broadcastEvent sends an event to all connected clients // broadcastEvent sends an event to all connected clients
func (s *Server) broadcastEvent(event derby.Event) { func (s *Server) broadcastRaceEvent(event derby.Event) {
var heatResult models.HeatResult
switch event.Type { switch event.Type {
case derby.EventRaceStart: case derby.EventRaceStart:
s.logger.Info("Broadcasting race start event") s.logger.Info("Broadcasting race start event")
s.sendEventToAllClients("event: race-status\ndata: <div id='status-indicator' class='badge bg-success'>Race Running</div>") s.sendRaceEventToAllClients("event: race-status\ndata: <div id='status-indicator' class='badge bg-success'>Race Running</div>")
heatGroup, _ := s.db.GetCurrentRacingGroup()
heatResult.GroupID = heatGroup.ID
heatResult.HeatNumber, _ = s.db.GetCurrentHeatNumber(heatGroup.ID)
case derby.EventLaneFinish: case derby.EventLaneFinish:
s.logger.Info("Broadcasting lane finish event", s.logger.Info("Broadcasting lane finish event",
"lane", event.Event.(derby.Result).Lane, "lane", event.Result.Lane,
"time", event.Event.(derby.Result).Time, "time", event.Result.Time,
"place", event.Event.(derby.Result).FinishPlace) "place", event.Result.FinishPlace)
switch event.Result.Lane {
case 1:
heatResult.Lane1Time = event.Result.Time
heatResult.Lane1Position = event.Result.FinishPlace
case 2:
heatResult.Lane2Time = event.Result.Time
heatResult.Lane2Position = event.Result.FinishPlace
case 3:
heatResult.Lane3Time = event.Result.Time
heatResult.Lane3Position = event.Result.FinishPlace
case 4:
heatResult.Lane4Time = event.Result.Time
heatResult.Lane4Position = event.Result.FinishPlace
}
s.sendRaceEventToAllClients(fmt.Sprintf("event: lane-%d-time\ndata: %.4f", event.Result.Lane, event.Result.Time))
s.sendRaceEventToAllClients(fmt.Sprintf("event: lane-%d-position\ndata: %d", event.Result.Lane, event.Result.FinishPlace))
s.sendEventToAllClients(fmt.Sprintf("event: lane-%d-time\ndata: %.4f", event.Event.(derby.Result).Lane, event.Event.(derby.Result).Time)) case derby.EventRaceComplete:
s.sendEventToAllClients(fmt.Sprintf("event: lane-%d-position\ndata: %d", event.Event.(derby.Result).Lane, event.Event.(derby.Result).FinishPlace)) s.logger.Info("Broadcasting race complete event")
s.sendRaceEventToAllClients("event: race-status\ndata: <div id='status-indicator' class='badge bg-info'>Race Complete</div>")
s.db.SaveHeatResult(heatResult)
}
}
case derby.EventHeatChanged: // broadcastEvent sends an event to all connected clients
func (s *Server) broadcastAdminEvent(event models.AdminEvent) {
switch event.Type {
case models.EventHeatChanged:
s.logger.Info("Broadcasting heat changed event") s.logger.Info("Broadcasting heat changed event")
s.sendEventToAllClients(fmt.Sprintf("event: heat-changed-number\ndata: %d out of %d", event.Event.(models.HeatData).HeatNumber, event.Event.(models.HeatData).TotalHeats)) if heatData, ok := event.Event.(models.HeatData); ok {
s.sendEventToAllClients("event: race-status\ndata: <div id='status-indicator' class='badge bg-secondary'>Idle</div>") component := templates.CurrentHeatDisplay(&heatData)
var sb strings.Builder
err := component.Render(context.Background(), &sb)
if err != nil {
s.logger.Error("Failed to render current heat display", "error", err)
}
s.sendAdminEventToAllClients(fmt.Sprintf("event: current-heat\ndata: %s", sb.String()))
nextHeatData, _ := s.db.GetHeatData(heatData.Group.ID, heatData.HeatNumber+1)
component = templates.NextHeatDisplay(nextHeatData)
var sb2 strings.Builder
err = component.Render(context.Background(), &sb2)
if err != nil {
s.logger.Error("Failed to render next heat display", "error", err)
}
s.sendAdminEventToAllClients(fmt.Sprintf("event: next-heat\ndata: %s", sb2.String()))
s.sendAdminEventToAllClients(fmt.Sprintf("event: heat-number\ndata: Current Heat: %d of %d", heatData.HeatNumber, heatData.TotalHeats))
} else {
s.logger.Error("Failed to convert event to HeatData")
}
case derby.EventGroupChanged: case models.EventGroupChanged:
s.logger.Info("Broadcasting group changed event") s.logger.Info("Broadcasting group changed event",
s.sendEventToAllClients("event: group-changed\ndata: <div id='status-indicator' class='badge bg-success'>Group Changed</div>") "group", event.Event)
case derby.EventRaceComplete: if heatResults, ok := event.Event.([]models.HeatResult); ok {
s.logger.Info("Broadcasting race complete event") component := templates.ResultsDisplay(heatResults)
s.sendEventToAllClients("event: race-status\ndata: <div id='status-indicator' class='badge bg-info'>Race Complete</div>") var sb strings.Builder
err := component.Render(context.Background(), &sb)
if err != nil {
s.logger.Error("Failed to render current heat results", "error", err)
}
s.sendAdminEventToAllClients(fmt.Sprintf("event: results\ndata: %s", sb.String()))
}
} }
} }
func (s *Server) sendEventToAllClients(message string) { func (s *Server) sendRaceEventToAllClients(message string) {
if message == "" { if message == "" {
return return
} }
@ -274,6 +323,26 @@ func (s *Server) sendEventToAllClients(message string) {
s.clientsMux.Unlock() s.clientsMux.Unlock()
} }
func (s *Server) sendAdminEventToAllClients(message string) {
if message == "" {
return
}
// Send to all clients
s.adminclientsMux.Lock()
clientCount := len(s.adminclients)
sentCount := 0
for clientChan := range s.adminclients {
clientChan <- message
sentCount++
}
s.logger.Debug("Event broadcast complete",
"sentCount", sentCount,
"totalClients", clientCount,
"message", message)
s.adminclientsMux.Unlock()
}
// handleIndex handles the index page // handleIndex handles the index page
func (s *Server) handleIndex() http.HandlerFunc { func (s *Server) handleIndex() http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) { return func(w http.ResponseWriter, r *http.Request) {
@ -336,9 +405,6 @@ func (s *Server) handleEvents() http.HandlerFunc {
w.Header().Set("Connection", "keep-alive") w.Header().Set("Connection", "keep-alive")
w.Header().Set("Access-Control-Allow-Origin", "*") w.Header().Set("Access-Control-Allow-Origin", "*")
// Debug message to confirm connection
fmt.Fprintf(w, "event: debug\ndata: {\"message\":\"SSE connection established\"}\n\n")
// Flush headers to ensure they're sent to the client // Flush headers to ensure they're sent to the client
if flusher, ok := w.(http.Flusher); ok { if flusher, ok := w.(http.Flusher); ok {
flusher.Flush() flusher.Flush()
@ -391,6 +457,67 @@ func (s *Server) handleEvents() http.HandlerFunc {
} }
} }
// handleAdminEvents handles SSE events
func (s *Server) handleAdminEvents() http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
// Set headers for SSE
w.Header().Set("Content-Type", "text/event-stream")
w.Header().Set("Cache-Control", "no-cache")
w.Header().Set("Connection", "keep-alive")
w.Header().Set("Access-Control-Allow-Origin", "*")
// Flush headers to ensure they're sent to the client
if flusher, ok := w.(http.Flusher); ok {
flusher.Flush()
} else {
http.Error(w, "Streaming unsupported!", http.StatusInternalServerError)
return
}
// Create a channel for this client
clientChan := make(chan string, 10)
// Add client to map with mutex protection
s.adminclientsMux.Lock()
s.adminclients[clientChan] = true
clientCount := len(s.adminclients)
s.adminclientsMux.Unlock()
s.logger.Info("New client connected",
"clientIP", r.RemoteAddr,
"totalClients", clientCount)
// Remove client when connection is closed
defer func() {
s.adminclientsMux.Lock()
delete(s.adminclients, clientChan)
remainingClients := len(s.adminclients)
s.adminclientsMux.Unlock()
close(clientChan)
s.logger.Info("Client disconnected",
"clientIP", r.RemoteAddr,
"remainingClients", remainingClients)
}()
// Keep connection open and send events as they arrive
for {
select {
case msg, ok := <-clientChan:
if !ok {
return
}
fmt.Fprintf(w, "%s\n\n", msg)
if flusher, ok := w.(http.Flusher); ok {
flusher.Flush()
}
case <-r.Context().Done():
return
}
}
}
}
// handleAdmin renders the admin page // handleAdmin renders the admin page
func (s *Server) handleAdmin() http.HandlerFunc { func (s *Server) handleAdmin() http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) { return func(w http.ResponseWriter, r *http.Request) {
@ -937,29 +1064,6 @@ func (s *Server) handleRacersList() http.HandlerFunc {
} }
} }
// handleAdminEvents handles admin events with SSE
func (s *Server) handleAdminEvents() http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
// Set SSE headers
w.Header().Set("Content-Type", "text/event-stream")
w.Header().Set("Cache-Control", "no-cache")
w.Header().Set("Connection", "keep-alive")
// Send events to client
for {
select {
case event := <-s.adminEvents:
fmt.Fprintf(w, "data: %s\n\n", event)
if f, ok := w.(http.Flusher); ok {
f.Flush()
}
case <-r.Context().Done():
return
}
}
}
}
// handleValidateCarNumber handles the validate car number API endpoint // handleValidateCarNumber handles the validate car number API endpoint
func (s *Server) handleValidateCarNumber() http.HandlerFunc { func (s *Server) handleValidateCarNumber() http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) { return func(w http.ResponseWriter, r *http.Request) {
@ -1272,28 +1376,19 @@ func (s *Server) handleNextHeat() http.HandlerFunc {
return return
} }
// Reset the clock heatData, _ := s.db.GetHeatData(currentGroup.ID, currentHeatNum+1)
if err := s.clock.Reset(); err != nil {
s.logger.Error("Failed to reset clock", "error", err)
}
// Broadcast event to admin page // Broadcast event to admin page
s.broadcastAdminEvent("heat-changed") s.broadcastAdminEvent(models.AdminEvent{
Type: models.EventHeatChanged,
Event: heatData,
})
w.WriteHeader(http.StatusOK) w.WriteHeader(http.StatusOK)
json.NewEncoder(w).Encode(map[string]string{"status": "success"}) json.NewEncoder(w).Encode(map[string]string{"status": "success"})
} }
} }
// broadcastAdminEvent sends an event to all admin clients
func (s *Server) broadcastAdminEvent(event string) {
// Create a simple event message
eventMsg := fmt.Sprintf(`{"event":"%s"}`, event)
// Send to all admin clients
s.adminEvents <- eventMsg
}
// handlePreviousHeat goes back to the previous heat // handlePreviousHeat goes back to the previous heat
func (s *Server) handlePreviousHeat() http.HandlerFunc { func (s *Server) handlePreviousHeat() http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) { return func(w http.ResponseWriter, r *http.Request) {
@ -1326,61 +1421,13 @@ func (s *Server) handlePreviousHeat() http.HandlerFunc {
return return
} }
// Reset the clock heatData, _ := s.db.GetHeatData(currentGroup.ID, currentHeatNum-1)
if err := s.clock.Reset(); err != nil {
s.logger.Error("Failed to reset clock", "error", err)
}
// Broadcast event to admin page // Broadcast event to admin page
s.broadcastAdminEvent("heat-changed") s.broadcastAdminEvent(models.AdminEvent{
Type: models.EventHeatChanged,
w.WriteHeader(http.StatusOK) Event: heatData,
json.NewEncoder(w).Encode(map[string]string{"status": "success"}) })
}
}
// handleSaveHeatResult saves the result of a heat
func (s *Server) handleSaveHeatResult() http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
// Parse request body
var request struct {
GroupID int64 `json:"group_id"`
HeatNumber int `json:"heat_number"`
Lane1Time float64 `json:"lane1_time"`
Lane1Position int `json:"lane1_position"`
Lane2Time float64 `json:"lane2_time"`
Lane2Position int `json:"lane2_position"`
Lane3Time float64 `json:"lane3_time"`
Lane3Position int `json:"lane3_position"`
Lane4Time float64 `json:"lane4_time"`
Lane4Position int `json:"lane4_position"`
}
if err := json.NewDecoder(r.Body).Decode(&request); err != nil {
http.Error(w, "Invalid request body", http.StatusBadRequest)
return
}
// Create heat result
result := models.HeatResult{
GroupID: request.GroupID,
HeatNumber: request.HeatNumber,
Lane1Time: request.Lane1Time,
Lane1Position: request.Lane1Position,
Lane2Time: request.Lane2Time,
Lane2Position: request.Lane2Position,
Lane3Time: request.Lane3Time,
Lane3Position: request.Lane3Position,
Lane4Time: request.Lane4Time,
Lane4Position: request.Lane4Position,
}
// Save heat result
if err := s.db.SaveHeatResult(result); err != nil {
s.logger.Error("Failed to save heat result", "error", err)
http.Error(w, "Failed to save heat result", http.StatusInternalServerError)
return
}
w.WriteHeader(http.StatusOK) w.WriteHeader(http.StatusOK)
json.NewEncoder(w).Encode(map[string]string{"status": "success"}) json.NewEncoder(w).Encode(map[string]string{"status": "success"})
@ -1413,13 +1460,13 @@ func (s *Server) handleRerunHeat() http.HandlerFunc {
return return
} }
// Reset the clock heatData, _ := s.db.GetHeatData(currentGroup.ID, currentHeatNum)
if err := s.clock.Reset(); err != nil {
s.logger.Error("Failed to reset clock", "error", err)
}
// Broadcast event to admin page // Broadcast event to admin page
s.broadcastAdminEvent("heat-rerun") s.broadcastAdminEvent(models.AdminEvent{
Type: models.EventHeatChanged,
Event: heatData,
})
w.WriteHeader(http.StatusOK) w.WriteHeader(http.StatusOK)
json.NewEncoder(w).Encode(map[string]string{"status": "success"}) json.NewEncoder(w).Encode(map[string]string{"status": "success"})
@ -1446,13 +1493,20 @@ func (s *Server) handleSetRacingGroup() http.HandlerFunc {
return return
} }
// Reset the clock heatData, _ := s.db.GetHeatData(request.GroupID, 1)
if err := s.clock.Reset(); err != nil {
s.logger.Error("Failed to reset clock", "error", err)
}
// Broadcast event to admin page // Broadcast event to admin page
s.broadcastAdminEvent("group-changed") s.broadcastAdminEvent(models.AdminEvent{
Type: models.EventHeatChanged,
Event: heatData,
})
heatResults, _ := s.db.GetHeatResults(request.GroupID)
s.broadcastAdminEvent(models.AdminEvent{
Type: models.EventGroupChanged,
Event: heatResults,
})
w.WriteHeader(http.StatusOK) w.WriteHeader(http.StatusOK)
json.NewEncoder(w).Encode(map[string]string{"status": "success"}) json.NewEncoder(w).Encode(map[string]string{"status": "success"})

@ -29,7 +29,7 @@ templ RaceManage(heatData *models.HeatData, nextHeat *models.HeatData, groups []
</div> </div>
<div class="mb-3"> <div class="mb-3">
<h5>Current Heat: { strconv.Itoa(heatData.HeatNumber) } of { strconv.Itoa(heatData.TotalHeats) }</h5> <h5 hx-ext="sse" sse-connect="/api/admin/events" sse-swap="heat-number">Current Heat: { strconv.Itoa(heatData.HeatNumber) } of { strconv.Itoa(heatData.TotalHeats) }</h5>
<div class="btn-group" role="group"> <div class="btn-group" role="group">
<button id="prev-heat-btn" class="btn btn-secondary" hx-post="/api/race/previous-heat" hx-swap="none" disabled?={ heatData.HeatNumber <= 1 }> <button id="prev-heat-btn" class="btn btn-secondary" hx-post="/api/race/previous-heat" hx-swap="none" disabled?={ heatData.HeatNumber <= 1 }>
<i class="bi bi-arrow-left"></i> Previous Heat <i class="bi bi-arrow-left"></i> Previous Heat
@ -43,7 +43,6 @@ templ RaceManage(heatData *models.HeatData, nextHeat *models.HeatData, groups []
<div class="mb-3"> <div class="mb-3">
<h5>Timer Control</h5> <h5>Timer Control</h5>
<div class="d-flex align-items-center mb-2"> <div class="d-flex align-items-center mb-2">
<h3 id="timer" class="me-3">0.000</h3>
<span id="status-indicator" class="badge mb-3 bg-secondary">Ready</span> <span id="status-indicator" class="badge mb-3 bg-secondary">Ready</span>
</div> </div>
<div class="btn-group" role="group"> <div class="btn-group" role="group">
@ -66,7 +65,25 @@ templ RaceManage(heatData *models.HeatData, nextHeat *models.HeatData, groups []
</div> </div>
</div> </div>
<div class="col-md-6"> <div class="col-md-6" hx-ext="sse" sse-connect="/api/admin/events" sse-swap="results">
@ResultsDisplay(results)
</div>
</div>
<!-- Current Heat Display -->
<div hx-ext="sse" sse-connect="/api/admin/events" sse-swap="current-heat">
@CurrentHeatDisplay(heatData)
</div>
<!-- Next Heat Preview -->
<div hx-ext="sse" sse-connect="/api/admin/events" sse-swap="next-heat">
@NextHeatDisplay(nextHeat)
</div>
</div>
}
}
templ ResultsDisplay(results []models.HeatResult) {
<div class="card"> <div class="card">
<div class="card-header bg-primary text-white"> <div class="card-header bg-primary text-white">
<h4 class="mb-0">Heat Results</h4> <h4 class="mb-0">Heat Results</h4>
@ -98,51 +115,10 @@ templ RaceManage(heatData *models.HeatData, nextHeat *models.HeatData, groups []
</div> </div>
</div> </div>
</div> </div>
</div>
</div>
<!-- Current Heat Display -->
@currentHeatDisplay(heatData)
<!-- Next Heat Preview -->
if nextHeat != nil {
<div class="card mb-4">
<div class="card-header bg-secondary text-white">
<h4 class="mb-0">Next Heat: { strconv.Itoa(nextHeat.HeatNumber) }</h4>
</div>
<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.Lane1 != nil {
@raceNextHeatRow(*nextHeat.Lane1)
}
if nextHeat.Lane2 != nil {
@raceNextHeatRow(*nextHeat.Lane2)
}
if nextHeat.Lane3 != nil {
@raceNextHeatRow(*nextHeat.Lane3)
}
if nextHeat.Lane4 != nil {
@raceNextHeatRow(*nextHeat.Lane4)
}
</tbody>
</table>
</div>
</div>
}
</div>
}
} }
// Find the current heat // Find the current heat
templ currentHeatDisplay(heatData *models.HeatData) { templ CurrentHeatDisplay(heatData *models.HeatData) {
<div class="card mb-4"> <div class="card mb-4">
<div class="card-header bg-primary text-white"> <div class="card-header bg-primary text-white">
<h4 class="mb-0">Current Heat: { strconv.Itoa(heatData.HeatNumber) }</h4> <h4 class="mb-0">Current Heat: { strconv.Itoa(heatData.HeatNumber) }</h4>
@ -178,10 +154,10 @@ templ raceLaneInfo(laneData models.LaneData) {
<p class="card-text">Car #: { laneData.CarNum }</p> <p class="card-text">Car #: { laneData.CarNum }</p>
<div class="d-flex justify-content-between"> <div class="d-flex justify-content-between">
<div> <div>
<strong>Time:</strong> <span>{ fmt.Sprintf("%.3f", laneData.Time) }</span> <strong>Time:</strong> <span hx-ext="sse" sse-connect="/api/events" sse-swap="lane-{ laneData.Lane }-time">{ fmt.Sprintf("%.3f", laneData.Time) }</span>
</div> </div>
<div> <div>
<strong>Position:</strong> <span>{ strconv.Itoa(laneData.Place) }</span> <strong>Position:</strong> <span hx-ext="sse" sse-connect="/api/events" sse-swap="lane-{ laneData.Lane }-position">{ strconv.Itoa(laneData.Place) }</span>
</div> </div>
</div> </div>
</div> </div>
@ -189,6 +165,41 @@ templ raceLaneInfo(laneData models.LaneData) {
</div> </div>
} }
templ NextHeatDisplay(nextHeat *models.HeatData) {
if nextHeat != nil {
<div class="card mb-4">
<div class="card-header bg-secondary text-white">
<h4 class="mb-0">Next Heat: { strconv.Itoa(nextHeat.HeatNumber) }</h4>
</div>
<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.Lane1 != nil {
@raceNextHeatRow(*nextHeat.Lane1)
}
if nextHeat.Lane2 != nil {
@raceNextHeatRow(*nextHeat.Lane2)
}
if nextHeat.Lane3 != nil {
@raceNextHeatRow(*nextHeat.Lane3)
}
if nextHeat.Lane4 != nil {
@raceNextHeatRow(*nextHeat.Lane4)
}
</tbody>
</table>
</div>
</div>
}
}
// Helper template for displaying a racer in the next heat // Helper template for displaying a racer in the next heat
templ nextHeatRacer(laneData models.LaneData) { templ nextHeatRacer(laneData models.LaneData) {
<tr> <tr>

@ -94,14 +94,14 @@ func RaceManage(heatData *models.HeatData, nextHeat *models.HeatData, groups []m
return templ_7745c5c3_Err return templ_7745c5c3_Err
} }
} }
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 7, "</select></div><div class=\"mb-3\"><h5>Current Heat: ") templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 7, "</select></div><div class=\"mb-3\"><h5 hx-ext=\"sse\" sse-connect=\"/api/admin/events\" sse-swap=\"heat-number\">Current Heat: ")
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err return templ_7745c5c3_Err
} }
var templ_7745c5c3_Var5 string var templ_7745c5c3_Var5 string
templ_7745c5c3_Var5, templ_7745c5c3_Err = templ.JoinStringErrs(strconv.Itoa(heatData.HeatNumber)) templ_7745c5c3_Var5, templ_7745c5c3_Err = templ.JoinStringErrs(strconv.Itoa(heatData.HeatNumber))
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `web/templates/race_manage.templ`, Line: 32, Col: 85} return templ.Error{Err: templ_7745c5c3_Err, FileName: `web/templates/race_manage.templ`, Line: 32, Col: 153}
} }
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var5)) _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var5))
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
@ -114,7 +114,7 @@ func RaceManage(heatData *models.HeatData, nextHeat *models.HeatData, groups []m
var templ_7745c5c3_Var6 string var templ_7745c5c3_Var6 string
templ_7745c5c3_Var6, templ_7745c5c3_Err = templ.JoinStringErrs(strconv.Itoa(heatData.TotalHeats)) templ_7745c5c3_Var6, templ_7745c5c3_Err = templ.JoinStringErrs(strconv.Itoa(heatData.TotalHeats))
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `web/templates/race_manage.templ`, Line: 32, Col: 126} return templ.Error{Err: templ_7745c5c3_Err, FileName: `web/templates/race_manage.templ`, Line: 32, Col: 194}
} }
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var6)) _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var6))
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
@ -140,217 +140,211 @@ func RaceManage(heatData *models.HeatData, nextHeat *models.HeatData, groups []m
return templ_7745c5c3_Err return templ_7745c5c3_Err
} }
} }
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 13, ">Next Heat <i class=\"bi bi-arrow-right\"></i></button></div></div><div class=\"mb-3\"><h5>Timer Control</h5><div class=\"d-flex align-items-center mb-2\"><h3 id=\"timer\" class=\"me-3\">0.000</h3><span id=\"status-indicator\" class=\"badge mb-3 bg-secondary\">Ready</span></div><div class=\"btn-group\" role=\"group\"><button class=\"btn btn-warning\" onclick=\"resetTimer()\"><i class=\"bi bi-arrow-repeat\"></i> Reset Timer</button> <button class=\"btn btn-danger\" onclick=\"forceEndHeat()\"><i class=\"bi bi-flag-fill\"></i> Force End</button> <button class=\"btn btn-info\" hx-post=\"/api/race/rerun-heat\" hx-swap=\"none\"><i class=\"bi bi-arrow-counterclockwise\"></i> Re-Run Heat</button></div></div><div id=\"gate-status\" class=\"alert alert-secondary\"><strong>Gate Status:</strong> <span id=\"gate-status-text\">Unknown</span></div></div></div></div><div class=\"col-md-6\"><div class=\"card\"><div class=\"card-header bg-primary text-white\"><h4 class=\"mb-0\">Heat Results</h4></div><div class=\"card-body\"><div class=\"table-responsive\"><table class=\"table table-striped\"><thead><tr><th>Heat</th><th>Lane 1</th><th>Lane 2</th><th>Lane 3</th><th>Lane 4</th></tr></thead> <tbody>") templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 13, ">Next Heat <i class=\"bi bi-arrow-right\"></i></button></div></div><div class=\"mb-3\"><h5>Timer Control</h5><div class=\"d-flex align-items-center mb-2\"><span id=\"status-indicator\" class=\"badge mb-3 bg-secondary\">Ready</span></div><div class=\"btn-group\" role=\"group\"><button class=\"btn btn-warning\" onclick=\"resetTimer()\"><i class=\"bi bi-arrow-repeat\"></i> Reset Timer</button> <button class=\"btn btn-danger\" onclick=\"forceEndHeat()\"><i class=\"bi bi-flag-fill\"></i> Force End</button> <button class=\"btn btn-info\" hx-post=\"/api/race/rerun-heat\" hx-swap=\"none\"><i class=\"bi bi-arrow-counterclockwise\"></i> Re-Run Heat</button></div></div><div id=\"gate-status\" class=\"alert alert-secondary\"><strong>Gate Status:</strong> <span id=\"gate-status-text\">Unknown</span></div></div></div></div><div class=\"col-md-6\" hx-ext=\"sse\" sse-connect=\"/api/admin/events\" sse-swap=\"results\">")
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err return templ_7745c5c3_Err
} }
for _, result := range results { templ_7745c5c3_Err = ResultsDisplay(results).Render(ctx, templ_7745c5c3_Buffer)
var templ_7745c5c3_Var7 = []any{"table-primary"}
templ_7745c5c3_Err = templ.RenderCSSItems(ctx, templ_7745c5c3_Buffer, templ_7745c5c3_Var7...)
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err return templ_7745c5c3_Err
} }
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 14, "<tr class=\"") templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 14, "</div></div><!-- Current Heat Display --><div hx-ext=\"sse\" sse-connect=\"/api/admin/events\" sse-swap=\"current-heat\">")
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err return templ_7745c5c3_Err
} }
var templ_7745c5c3_Var8 string templ_7745c5c3_Err = CurrentHeatDisplay(heatData).Render(ctx, templ_7745c5c3_Buffer)
templ_7745c5c3_Var8, templ_7745c5c3_Err = templ.JoinStringErrs(templ.CSSClasses(templ_7745c5c3_Var7).String())
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `web/templates/race_manage.templ`, Line: 1, Col: 0} return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 15, "</div><!-- Next Heat Preview --><div hx-ext=\"sse\" sse-connect=\"/api/admin/events\" sse-swap=\"next-heat\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
} }
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var8)) templ_7745c5c3_Err = NextHeatDisplay(nextHeat).Render(ctx, templ_7745c5c3_Buffer)
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err return templ_7745c5c3_Err
} }
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 15, "\"><td>") templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 16, "</div></div>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
return nil
})
templ_7745c5c3_Err = Layout("Race Management").Render(templ.WithChildren(ctx, templ_7745c5c3_Var2), templ_7745c5c3_Buffer)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
return nil
})
}
func ResultsDisplay(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_Var7 := templ.GetChildren(ctx)
if templ_7745c5c3_Var7 == nil {
templ_7745c5c3_Var7 = templ.NopComponent
}
ctx = templ.ClearChildren(ctx)
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 17, "<div class=\"card\"><div class=\"card-header bg-primary text-white\"><h4 class=\"mb-0\">Heat Results</h4></div><div class=\"card-body\"><div class=\"table-responsive\"><table class=\"table table-striped\"><thead><tr><th>Heat</th><th>Lane 1</th><th>Lane 2</th><th>Lane 3</th><th>Lane 4</th></tr></thead> <tbody>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
for _, result := range results {
var templ_7745c5c3_Var8 = []any{"table-primary"}
templ_7745c5c3_Err = templ.RenderCSSItems(ctx, templ_7745c5c3_Buffer, templ_7745c5c3_Var8...)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 18, "<tr class=\"")
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err return templ_7745c5c3_Err
} }
var templ_7745c5c3_Var9 string var templ_7745c5c3_Var9 string
templ_7745c5c3_Var9, templ_7745c5c3_Err = templ.JoinStringErrs(strconv.Itoa(result.HeatNumber)) templ_7745c5c3_Var9, templ_7745c5c3_Err = templ.JoinStringErrs(templ.CSSClasses(templ_7745c5c3_Var8).String())
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `web/templates/race_manage.templ`, Line: 89, Col: 85} return templ.Error{Err: templ_7745c5c3_Err, FileName: `web/templates/race_manage.templ`, Line: 1, Col: 0}
} }
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var9)) _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var9))
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err return templ_7745c5c3_Err
} }
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 16, "</td><td>") templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 19, "\"><td>")
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err return templ_7745c5c3_Err
} }
var templ_7745c5c3_Var10 string var templ_7745c5c3_Var10 string
templ_7745c5c3_Var10, templ_7745c5c3_Err = templ.JoinStringErrs(fmt.Sprintf("%.4f", result.Lane1Time)) templ_7745c5c3_Var10, templ_7745c5c3_Err = templ.JoinStringErrs(strconv.Itoa(result.HeatNumber))
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `web/templates/race_manage.templ`, Line: 90, Col: 91} return templ.Error{Err: templ_7745c5c3_Err, FileName: `web/templates/race_manage.templ`, Line: 106, Col: 69}
} }
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var10)) _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var10))
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err return templ_7745c5c3_Err
} }
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 17, " (") templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 20, "</td><td>")
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err return templ_7745c5c3_Err
} }
var templ_7745c5c3_Var11 string var templ_7745c5c3_Var11 string
templ_7745c5c3_Var11, templ_7745c5c3_Err = templ.JoinStringErrs(strconv.Itoa(result.Lane1Position)) templ_7745c5c3_Var11, templ_7745c5c3_Err = templ.JoinStringErrs(fmt.Sprintf("%.4f", result.Lane1Time))
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `web/templates/race_manage.templ`, Line: 90, Col: 131} return templ.Error{Err: templ_7745c5c3_Err, FileName: `web/templates/race_manage.templ`, Line: 107, Col: 75}
} }
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var11)) _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var11))
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err return templ_7745c5c3_Err
} }
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 18, ")</td><td>") templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 21, " (")
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err return templ_7745c5c3_Err
} }
var templ_7745c5c3_Var12 string var templ_7745c5c3_Var12 string
templ_7745c5c3_Var12, templ_7745c5c3_Err = templ.JoinStringErrs(fmt.Sprintf("%.4f", result.Lane2Time)) templ_7745c5c3_Var12, templ_7745c5c3_Err = templ.JoinStringErrs(strconv.Itoa(result.Lane1Position))
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `web/templates/race_manage.templ`, Line: 91, Col: 91} return templ.Error{Err: templ_7745c5c3_Err, FileName: `web/templates/race_manage.templ`, Line: 107, Col: 115}
} }
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var12)) _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var12))
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err return templ_7745c5c3_Err
} }
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 19, " (") templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 22, ")</td><td>")
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err return templ_7745c5c3_Err
} }
var templ_7745c5c3_Var13 string var templ_7745c5c3_Var13 string
templ_7745c5c3_Var13, templ_7745c5c3_Err = templ.JoinStringErrs(strconv.Itoa(result.Lane2Position)) templ_7745c5c3_Var13, templ_7745c5c3_Err = templ.JoinStringErrs(fmt.Sprintf("%.4f", result.Lane2Time))
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `web/templates/race_manage.templ`, Line: 91, Col: 131} return templ.Error{Err: templ_7745c5c3_Err, FileName: `web/templates/race_manage.templ`, Line: 108, Col: 75}
} }
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var13)) _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var13))
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err return templ_7745c5c3_Err
} }
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 20, ")</td><td>") templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 23, " (")
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err return templ_7745c5c3_Err
} }
var templ_7745c5c3_Var14 string var templ_7745c5c3_Var14 string
templ_7745c5c3_Var14, templ_7745c5c3_Err = templ.JoinStringErrs(fmt.Sprintf("%.4f", result.Lane3Time)) templ_7745c5c3_Var14, templ_7745c5c3_Err = templ.JoinStringErrs(strconv.Itoa(result.Lane2Position))
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `web/templates/race_manage.templ`, Line: 92, Col: 91} return templ.Error{Err: templ_7745c5c3_Err, FileName: `web/templates/race_manage.templ`, Line: 108, Col: 115}
} }
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var14)) _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var14))
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err return templ_7745c5c3_Err
} }
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 21, " (") templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 24, ")</td><td>")
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err return templ_7745c5c3_Err
} }
var templ_7745c5c3_Var15 string var templ_7745c5c3_Var15 string
templ_7745c5c3_Var15, templ_7745c5c3_Err = templ.JoinStringErrs(strconv.Itoa(result.Lane3Position)) templ_7745c5c3_Var15, templ_7745c5c3_Err = templ.JoinStringErrs(fmt.Sprintf("%.4f", result.Lane3Time))
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `web/templates/race_manage.templ`, Line: 92, Col: 131} return templ.Error{Err: templ_7745c5c3_Err, FileName: `web/templates/race_manage.templ`, Line: 109, Col: 75}
} }
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var15)) _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var15))
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err return templ_7745c5c3_Err
} }
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 22, ")</td><td>") templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 25, " (")
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err return templ_7745c5c3_Err
} }
var templ_7745c5c3_Var16 string var templ_7745c5c3_Var16 string
templ_7745c5c3_Var16, templ_7745c5c3_Err = templ.JoinStringErrs(fmt.Sprintf("%.4f", result.Lane4Time)) templ_7745c5c3_Var16, templ_7745c5c3_Err = templ.JoinStringErrs(strconv.Itoa(result.Lane3Position))
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `web/templates/race_manage.templ`, Line: 93, Col: 91} return templ.Error{Err: templ_7745c5c3_Err, FileName: `web/templates/race_manage.templ`, Line: 109, Col: 115}
} }
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var16)) _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var16))
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err return templ_7745c5c3_Err
} }
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 23, " (") templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 26, ")</td><td>")
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err return templ_7745c5c3_Err
} }
var templ_7745c5c3_Var17 string var templ_7745c5c3_Var17 string
templ_7745c5c3_Var17, templ_7745c5c3_Err = templ.JoinStringErrs(strconv.Itoa(result.Lane4Position)) templ_7745c5c3_Var17, templ_7745c5c3_Err = templ.JoinStringErrs(fmt.Sprintf("%.4f", result.Lane4Time))
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `web/templates/race_manage.templ`, Line: 93, Col: 131} return templ.Error{Err: templ_7745c5c3_Err, FileName: `web/templates/race_manage.templ`, Line: 110, Col: 75}
} }
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var17)) _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var17))
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err return templ_7745c5c3_Err
} }
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 24, ")</td></tr>") templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 27, " (")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 25, "</tbody></table></div></div></div></div></div><!-- Current Heat Display -->")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = currentHeatDisplay(heatData).Render(ctx, templ_7745c5c3_Buffer)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 26, "<!-- Next Heat Preview -->")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
if nextHeat != nil {
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 27, "<div class=\"card mb-4\"><div class=\"card-header bg-secondary text-white\"><h4 class=\"mb-0\">Next Heat: ")
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err return templ_7745c5c3_Err
} }
var templ_7745c5c3_Var18 string var templ_7745c5c3_Var18 string
templ_7745c5c3_Var18, templ_7745c5c3_Err = templ.JoinStringErrs(strconv.Itoa(nextHeat.HeatNumber)) templ_7745c5c3_Var18, templ_7745c5c3_Err = templ.JoinStringErrs(strconv.Itoa(result.Lane4Position))
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `web/templates/race_manage.templ`, Line: 111, Col: 87} return templ.Error{Err: templ_7745c5c3_Err, FileName: `web/templates/race_manage.templ`, Line: 110, Col: 115}
} }
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var18)) _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var18))
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err return templ_7745c5c3_Err
} }
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 28, "</h4></div><div class=\"table-responsive\"><table class=\"table table-striped\"><thead><tr><th>Lane</th><th>Racer</th><th>Car #</th></tr></thead> <tbody>") templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 28, ")</td></tr>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
if nextHeat.Lane1 != nil {
templ_7745c5c3_Err = raceNextHeatRow(*nextHeat.Lane1).Render(ctx, templ_7745c5c3_Buffer)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
}
if nextHeat.Lane2 != nil {
templ_7745c5c3_Err = raceNextHeatRow(*nextHeat.Lane2).Render(ctx, templ_7745c5c3_Buffer)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
}
if nextHeat.Lane3 != nil {
templ_7745c5c3_Err = raceNextHeatRow(*nextHeat.Lane3).Render(ctx, templ_7745c5c3_Buffer)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
}
if nextHeat.Lane4 != nil {
templ_7745c5c3_Err = raceNextHeatRow(*nextHeat.Lane4).Render(ctx, templ_7745c5c3_Buffer)
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err return templ_7745c5c3_Err
} }
} }
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 29, "</tbody></table></div></div>") templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 29, "</tbody></table></div></div></div>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 30, "</div>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
return nil
})
templ_7745c5c3_Err = Layout("Race Management").Render(templ.WithChildren(ctx, templ_7745c5c3_Var2), templ_7745c5c3_Buffer)
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err return templ_7745c5c3_Err
} }
@ -359,7 +353,7 @@ func RaceManage(heatData *models.HeatData, nextHeat *models.HeatData, groups []m
} }
// Find the current heat // Find the current heat
func currentHeatDisplay(heatData *models.HeatData) templ.Component { func CurrentHeatDisplay(heatData *models.HeatData) templ.Component {
return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) { 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 templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil { if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil {
@ -380,20 +374,20 @@ func currentHeatDisplay(heatData *models.HeatData) templ.Component {
templ_7745c5c3_Var19 = templ.NopComponent templ_7745c5c3_Var19 = templ.NopComponent
} }
ctx = templ.ClearChildren(ctx) ctx = templ.ClearChildren(ctx)
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 31, "<div class=\"card mb-4\"><div class=\"card-header bg-primary text-white\"><h4 class=\"mb-0\">Current Heat: ") templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 30, "<div class=\"card mb-4\"><div class=\"card-header bg-primary text-white\"><h4 class=\"mb-0\">Current Heat: ")
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err return templ_7745c5c3_Err
} }
var templ_7745c5c3_Var20 string var templ_7745c5c3_Var20 string
templ_7745c5c3_Var20, templ_7745c5c3_Err = templ.JoinStringErrs(strconv.Itoa(heatData.HeatNumber)) templ_7745c5c3_Var20, templ_7745c5c3_Err = templ.JoinStringErrs(strconv.Itoa(heatData.HeatNumber))
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `web/templates/race_manage.templ`, Line: 148, Col: 78} return templ.Error{Err: templ_7745c5c3_Err, FileName: `web/templates/race_manage.templ`, Line: 124, Col: 78}
} }
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var20)) _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var20))
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err return templ_7745c5c3_Err
} }
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 32, "</h4></div><div class=\"card-body\"><div class=\"row\">") templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 31, "</h4></div><div class=\"card-body\"><div class=\"row\">")
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err return templ_7745c5c3_Err
} }
@ -421,7 +415,7 @@ func currentHeatDisplay(heatData *models.HeatData) templ.Component {
return templ_7745c5c3_Err return templ_7745c5c3_Err
} }
} }
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 33, "</div></div></div>") templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 32, "</div></div></div>")
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err return templ_7745c5c3_Err
} }
@ -451,72 +445,72 @@ func raceLaneInfo(laneData models.LaneData) templ.Component {
templ_7745c5c3_Var21 = templ.NopComponent templ_7745c5c3_Var21 = templ.NopComponent
} }
ctx = templ.ClearChildren(ctx) ctx = templ.ClearChildren(ctx)
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 34, "<div class=\"col-md-3 mb-3\"><div class=\"card h-100\"><div class=\"card-header bg-secondary text-white\"><h5 class=\"mb-0\">Lane ") templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 33, "<div class=\"col-md-3 mb-3\"><div class=\"card h-100\"><div class=\"card-header bg-secondary text-white\"><h5 class=\"mb-0\">Lane ")
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err return templ_7745c5c3_Err
} }
var templ_7745c5c3_Var22 string var templ_7745c5c3_Var22 string
templ_7745c5c3_Var22, templ_7745c5c3_Err = templ.JoinStringErrs(strconv.Itoa(laneData.Lane)) templ_7745c5c3_Var22, templ_7745c5c3_Err = templ.JoinStringErrs(strconv.Itoa(laneData.Lane))
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `web/templates/race_manage.templ`, Line: 174, Col: 67} return templ.Error{Err: templ_7745c5c3_Err, FileName: `web/templates/race_manage.templ`, Line: 150, Col: 67}
} }
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var22)) _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var22))
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err return templ_7745c5c3_Err
} }
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 35, "</h5></div><div class=\"card-body\"><h5 class=\"card-title\">") templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 34, "</h5></div><div class=\"card-body\"><h5 class=\"card-title\">")
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err return templ_7745c5c3_Err
} }
var templ_7745c5c3_Var23 string var templ_7745c5c3_Var23 string
templ_7745c5c3_Var23, templ_7745c5c3_Err = templ.JoinStringErrs(laneData.Name) templ_7745c5c3_Var23, templ_7745c5c3_Err = templ.JoinStringErrs(laneData.Name)
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `web/templates/race_manage.templ`, Line: 177, Col: 54} return templ.Error{Err: templ_7745c5c3_Err, FileName: `web/templates/race_manage.templ`, Line: 153, Col: 54}
} }
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var23)) _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var23))
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err return templ_7745c5c3_Err
} }
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 36, "</h5><p class=\"card-text\">Car #: ") templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 35, "</h5><p class=\"card-text\">Car #: ")
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err return templ_7745c5c3_Err
} }
var templ_7745c5c3_Var24 string var templ_7745c5c3_Var24 string
templ_7745c5c3_Var24, templ_7745c5c3_Err = templ.JoinStringErrs(laneData.CarNum) templ_7745c5c3_Var24, templ_7745c5c3_Err = templ.JoinStringErrs(laneData.CarNum)
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `web/templates/race_manage.templ`, Line: 178, Col: 61} return templ.Error{Err: templ_7745c5c3_Err, FileName: `web/templates/race_manage.templ`, Line: 154, Col: 61}
} }
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var24)) _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var24))
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err return templ_7745c5c3_Err
} }
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 37, "</p><div class=\"d-flex justify-content-between\"><div><strong>Time:</strong> <span>") templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 36, "</p><div class=\"d-flex justify-content-between\"><div><strong>Time:</strong> <span>")
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err return templ_7745c5c3_Err
} }
var templ_7745c5c3_Var25 string var templ_7745c5c3_Var25 string
templ_7745c5c3_Var25, templ_7745c5c3_Err = templ.JoinStringErrs(fmt.Sprintf("%.3f", laneData.Time)) templ_7745c5c3_Var25, templ_7745c5c3_Err = templ.JoinStringErrs(fmt.Sprintf("%.3f", laneData.Time))
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `web/templates/race_manage.templ`, Line: 181, Col: 89} return templ.Error{Err: templ_7745c5c3_Err, FileName: `web/templates/race_manage.templ`, Line: 157, Col: 89}
} }
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var25)) _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var25))
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err return templ_7745c5c3_Err
} }
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 38, "</span></div><div><strong>Position:</strong> <span>") templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 37, "</span></div><div><strong>Position:</strong> <span>")
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err return templ_7745c5c3_Err
} }
var templ_7745c5c3_Var26 string var templ_7745c5c3_Var26 string
templ_7745c5c3_Var26, templ_7745c5c3_Err = templ.JoinStringErrs(strconv.Itoa(laneData.Place)) templ_7745c5c3_Var26, templ_7745c5c3_Err = templ.JoinStringErrs(strconv.Itoa(laneData.Place))
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `web/templates/race_manage.templ`, Line: 184, Col: 87} return templ.Error{Err: templ_7745c5c3_Err, FileName: `web/templates/race_manage.templ`, Line: 160, Col: 87}
} }
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var26)) _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var26))
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err return templ_7745c5c3_Err
} }
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 39, "</span></div></div></div></div></div>") templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 38, "</span></div></div></div></div></div>")
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err return templ_7745c5c3_Err
} }
@ -524,8 +518,7 @@ func raceLaneInfo(laneData models.LaneData) templ.Component {
}) })
} }
// Helper template for displaying a racer in the next heat func NextHeatDisplay(nextHeat *models.HeatData) templ.Component {
func nextHeatRacer(laneData models.LaneData) templ.Component {
return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) { 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 templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil { if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil {
@ -546,46 +539,119 @@ func nextHeatRacer(laneData models.LaneData) templ.Component {
templ_7745c5c3_Var27 = templ.NopComponent templ_7745c5c3_Var27 = templ.NopComponent
} }
ctx = templ.ClearChildren(ctx) ctx = templ.ClearChildren(ctx)
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 40, "<tr><td>") if nextHeat != nil {
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 39, "<div class=\"card mb-4\"><div class=\"card-header bg-secondary text-white\"><h4 class=\"mb-0\">Next Heat: ")
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err return templ_7745c5c3_Err
} }
var templ_7745c5c3_Var28 string var templ_7745c5c3_Var28 string
templ_7745c5c3_Var28, templ_7745c5c3_Err = templ.JoinStringErrs(strconv.Itoa(laneData.Lane)) templ_7745c5c3_Var28, templ_7745c5c3_Err = templ.JoinStringErrs(strconv.Itoa(nextHeat.HeatNumber))
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `web/templates/race_manage.templ`, Line: 195, Col: 41} return templ.Error{Err: templ_7745c5c3_Err, FileName: `web/templates/race_manage.templ`, Line: 172, Col: 79}
} }
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var28)) _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var28))
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err return templ_7745c5c3_Err
} }
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 41, "</td><td>") templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 40, "</h4></div><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 { if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err return templ_7745c5c3_Err
} }
var templ_7745c5c3_Var29 string if nextHeat.Lane1 != nil {
templ_7745c5c3_Var29, templ_7745c5c3_Err = templ.JoinStringErrs(laneData.Name) templ_7745c5c3_Err = raceNextHeatRow(*nextHeat.Lane1).Render(ctx, templ_7745c5c3_Buffer)
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `web/templates/race_manage.templ`, Line: 196, Col: 27} return templ_7745c5c3_Err
}
}
if nextHeat.Lane2 != nil {
templ_7745c5c3_Err = raceNextHeatRow(*nextHeat.Lane2).Render(ctx, templ_7745c5c3_Buffer)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
}
if nextHeat.Lane3 != nil {
templ_7745c5c3_Err = raceNextHeatRow(*nextHeat.Lane3).Render(ctx, templ_7745c5c3_Buffer)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
} }
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var29)) }
if nextHeat.Lane4 != nil {
templ_7745c5c3_Err = raceNextHeatRow(*nextHeat.Lane4).Render(ctx, templ_7745c5c3_Buffer)
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err return templ_7745c5c3_Err
} }
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 42, "</td><td>") }
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 41, "</tbody></table></div></div>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
}
return nil
})
}
// Helper template for displaying a racer in the next heat
func nextHeatRacer(laneData models.LaneData) 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_Var29 := templ.GetChildren(ctx)
if templ_7745c5c3_Var29 == nil {
templ_7745c5c3_Var29 = templ.NopComponent
}
ctx = templ.ClearChildren(ctx)
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 42, "<tr><td>")
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err return templ_7745c5c3_Err
} }
var templ_7745c5c3_Var30 string var templ_7745c5c3_Var30 string
templ_7745c5c3_Var30, templ_7745c5c3_Err = templ.JoinStringErrs(laneData.CarNum) templ_7745c5c3_Var30, templ_7745c5c3_Err = templ.JoinStringErrs(strconv.Itoa(laneData.Lane))
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `web/templates/race_manage.templ`, Line: 197, Col: 29} return templ.Error{Err: templ_7745c5c3_Err, FileName: `web/templates/race_manage.templ`, Line: 206, Col: 41}
} }
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var30)) _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var30))
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err return templ_7745c5c3_Err
} }
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 43, "</td></tr>") templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 43, "</td><td>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var31 string
templ_7745c5c3_Var31, templ_7745c5c3_Err = templ.JoinStringErrs(laneData.Name)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `web/templates/race_manage.templ`, Line: 207, Col: 27}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var31))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 44, "</td><td>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var32 string
templ_7745c5c3_Var32, templ_7745c5c3_Err = templ.JoinStringErrs(laneData.CarNum)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `web/templates/race_manage.templ`, Line: 208, Col: 29}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var32))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 45, "</td></tr>")
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err return templ_7745c5c3_Err
} }

@ -107,7 +107,7 @@ templ raceLaneCard(laneData models.LaneData) {
<div class="col-6"> <div class="col-6">
<div class="text-center"> <div class="text-center">
<h6>Time</h6> <h6>Time</h6>
<div class="display-6"> <div class="display-6" hx-ext="sse" sse-connect="/api/events" sse-swap="lane-{ laneData.Lane }-time">
{ fmt.Sprintf("%.3f", laneData.Time) } { fmt.Sprintf("%.3f", laneData.Time) }
</div> </div>
</div> </div>
@ -115,7 +115,7 @@ templ raceLaneCard(laneData models.LaneData) {
<div class="col-6"> <div class="col-6">
<div class="text-center"> <div class="text-center">
<h6>Position</h6> <h6>Position</h6>
<div class="display-6"> <div class="display-6" hx-ext="sse" sse-connect="/api/events" sse-swap="lane-{ laneData.Lane }-position">
{ strconv.Itoa(laneData.Place) } { strconv.Itoa(laneData.Place) }
</div> </div>
</div> </div>

Loading…
Cancel
Save