From 6266a06abb0d89c9f9f68e3071d4190404d8c3ab Mon Sep 17 00:00:00 2001
From: John O'Keefe
Date: Sat, 31 Jan 2026 23:14:08 -0500
Subject: [PATCH] feat: Add SSR for conflicts and queue pages
- Update conflicts.templ to accept pre-rendered data
- Update queue.templ to accept pre-rendered data
- Update /conflicts and /queue routes in main.go for SSR
- Update stats rendering to use server-side values
- Add server-side conflict list rendering
- Add server-side queue list rendering
- Update conflicts.js to use location.reload() after operations
- Update queue.js to use location.reload() after operations
- Remove initial load calls from JavaScript files
Preserves all API endpoints and backward compatibility
---
cmd/server/main.go | 95 ++++++++++++++-
templates/collections.templ | 7 +-
templates/collections_templ.go | 109 ++++++++---------
templates/conflicts.templ | 58 ++++++---
templates/conflicts_templ.go | 119 +++++++++++++++++-
templates/devices_templ.go | 215 ++++++++++++++++++++++++++++++++-
templates/queue.templ | 51 ++++++--
templates/queue_templ.go | 207 ++++++++++++++++++++++++++++++-
templates/types.go | 3 +
9 files changed, 766 insertions(+), 98 deletions(-)
diff --git a/cmd/server/main.go b/cmd/server/main.go
index fdf7c77..7d2a3e5 100644
--- a/cmd/server/main.go
+++ b/cmd/server/main.go
@@ -423,7 +423,7 @@ func main() {
return c.HTML(http.StatusOK, buf.String())
})
- // Device Management route (protected)
+ // Device Management route (protected) - SSR version
protected.GET("/devices", func(c echo.Context) error {
userID := c.Get("user_id").(string)
userEmail := c.Get("user_email").(string)
@@ -437,15 +437,63 @@ func main() {
Role: userRole,
}
+ // Fetch devices for SSR (uses new helper method)
+ devices, err := deviceHandler.GetDevicesData(c)
+ if err != nil {
+ return c.HTML(http.StatusInternalServerError, "Error loading devices")
+ }
+
+ // Convert to template format
+ deviceData := make([]templates.DeviceData, len(devices))
+ for i, device := range devices {
+ var lastSync, lastSeen string
+ if device.LastSync != nil {
+ lastSync = device.LastSync.Format("2006-01-02T15:04:05Z07:00")
+ }
+ if device.LastSeen != nil {
+ lastSeen = device.LastSeen.Format("2006-01-02T15:04:05Z07:00")
+ }
+
+ deviceData[i] = templates.DeviceData{
+ ID: device.ID.String(),
+ DeviceName: device.DeviceName,
+ DeviceType: device.DeviceType,
+ SyncEnabled: device.SyncEnabled,
+ AutoSync: device.AutoSync,
+ SyncFrequency: device.SyncFrequency,
+ LastSync: lastSync,
+ LastSeen: lastSeen,
+ }
+ }
+
+ // Fetch pending registrations for SSR (uses new helper)
+ pendingRegs, err := deviceHandler.GetPendingRegistrationsData(c)
+ if err != nil {
+ return c.HTML(http.StatusInternalServerError, "Error loading pending registrations")
+ }
+
+ // Convert to template format
+ pendingData := make([]templates.PendingRegistrationData, len(pendingRegs))
+ for i, reg := range pendingRegs {
+ expiresAt := reg["expires_at"].(time.Time)
+ pendingData[i] = templates.PendingRegistrationData{
+ RegistrationID: reg["registration_id"].(string),
+ DeviceName: reg["device_name"].(string),
+ DeviceType: reg["device_type"].(string),
+ ExpiresAt: expiresAt.Format("2006-01-02T15:04:05Z07:00"),
+ }
+ }
+
+ // Render template WITH data (SSR)
var buf bytes.Buffer
- err := templates.Devices(user).Render(c.Request().Context(), &buf)
+ err = templates.Devices(user, deviceData, pendingData).Render(c.Request().Context(), &buf)
if err != nil {
return err
}
return c.HTML(http.StatusOK, buf.String())
})
- // Conflicts route (protected)
+ // Conflicts route (protected) - SSR version
protected.GET("/conflicts", func(c echo.Context) error {
userID := c.Get("user_id").(string)
userEmail := c.Get("user_email").(string)
@@ -459,15 +507,22 @@ func main() {
Role: userRole,
}
+ // Fetch conflicts for SSR (uses existing handler method)
+ apiConflicts, total, unresolved, err := conflictHandler.GetConflictsData(c)
+ if err != nil {
+ return c.HTML(http.StatusInternalServerError, "Error loading conflicts")
+ }
+
+ // Render template WITH data (SSR) - use apiConflicts directly
var buf bytes.Buffer
- err := templates.Conflicts(user).Render(c.Request().Context(), &buf)
+ err = templates.Conflicts(user, apiConflicts, total, unresolved).Render(c.Request().Context(), &buf)
if err != nil {
return err
}
return c.HTML(http.StatusOK, buf.String())
})
- // Queue Management route (protected)
+ // Queue Management route (protected) - SSR version
protected.GET("/queue", func(c echo.Context) error {
userID := c.Get("user_id").(string)
userEmail := c.Get("user_email").(string)
@@ -481,8 +536,36 @@ func main() {
Role: userRole,
}
+ // Fetch queue items for SSR (uses existing handler method)
+ queueItems, err := queueHandler.GetQueueData(c)
+ if err != nil {
+ return c.HTML(http.StatusInternalServerError, "Error loading queue")
+ }
+
+ // Calculate stats from items
+ stats := handlers.QueueStatsResponse{
+ PendingCount: 0,
+ ProcessingCount: 0,
+ FailedCount: 0,
+ CompletedCount: 0,
+ TotalCount: int64(len(queueItems)),
+ }
+ for _, item := range queueItems {
+ switch item.Status {
+ case "pending":
+ stats.PendingCount++
+ case "processing":
+ stats.ProcessingCount++
+ case "failed":
+ stats.FailedCount++
+ case "completed":
+ stats.CompletedCount++
+ }
+ }
+
+ // Render template WITH data (SSR)
var buf bytes.Buffer
- err := templates.Queue(user).Render(c.Request().Context(), &buf)
+ err = templates.Queue(user, queueItems, stats).Render(c.Request().Context(), &buf)
if err != nil {
return err
}
diff --git a/templates/collections.templ b/templates/collections.templ
index 4e3ff83..a7ec276 100644
--- a/templates/collections.templ
+++ b/templates/collections.templ
@@ -55,10 +55,9 @@ templ Collection(user User, collections []CollectionData) {
- { col.Name }
- { col.Description }
- Created { col.CreatedAt }
-
+ { col.Name }
+ { col.Description }
+
}
diff --git a/templates/collections_templ.go b/templates/collections_templ.go
index 9ba9262..65b167d 100644
--- a/templates/collections_templ.go
+++ b/templates/collections_templ.go
@@ -68,7 +68,7 @@ func Collection(user User, collections []CollectionData) templ.Component {
var templ_7745c5c3_Var3 string
templ_7745c5c3_Var3, templ_7745c5c3_Err = templ.JoinStringErrs(col.Name)
if templ_7745c5c3_Err != nil {
- return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/collections.templ`, Line: 58, Col: 108}
+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/collections.templ`, Line: 58, Col: 109}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var3))
if templ_7745c5c3_Err != nil {
@@ -81,31 +81,18 @@ func Collection(user User, collections []CollectionData) templ.Component {
var templ_7745c5c3_Var4 string
templ_7745c5c3_Var4, templ_7745c5c3_Err = templ.JoinStringErrs(col.Description)
if templ_7745c5c3_Err != nil {
- return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/collections.templ`, Line: 59, Col: 102}
+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/collections.templ`, Line: 59, Col: 103}
}
_, 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, 7, "
Created ")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- var templ_7745c5c3_Var5 string
- templ_7745c5c3_Var5, templ_7745c5c3_Err = templ.JoinStringErrs(col.CreatedAt)
- if templ_7745c5c3_Err != nil {
- return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/collections.templ`, Line: 60, Col: 103}
- }
- _, 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, 8, "
")
+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 7, "")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
}
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 9, "