Add UI sync status indicator with pending badge

- Create htmx-powered partial template for sync status display
- Show Online/Offline indicator with color coding (green/red)
- Display pending changes count badge when there are unsynced items
- Add Sync button to push pending changes (appears only when needed)
- Auto-refresh every 30 seconds via htmx polling
- Replace JavaScript-based sync indicator with server-rendered partial
- Integrate SyncStatusPartial handler with template rendering

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
2026-02-02 06:38:23 +03:00
parent 1f739a3ab2
commit ec3c16f3fc
4 changed files with 81 additions and 74 deletions

View File

@@ -1,8 +1,10 @@
package handlers
import (
"html/template"
"log/slog"
"net/http"
"path/filepath"
"time"
"github.com/gin-gonic/gin"
@@ -16,15 +18,24 @@ type SyncHandler struct {
localDB *localdb.LocalDB
syncService *sync.Service
mariaDB *gorm.DB
tmpl *template.Template
}
// NewSyncHandler creates a new sync handler
func NewSyncHandler(localDB *localdb.LocalDB, syncService *sync.Service, mariaDB *gorm.DB) *SyncHandler {
func NewSyncHandler(localDB *localdb.LocalDB, syncService *sync.Service, mariaDB *gorm.DB, templatesPath string) (*SyncHandler, error) {
// Load sync_status partial template
partialPath := filepath.Join(templatesPath, "partials", "sync_status.html")
tmpl, err := template.ParseFiles(partialPath)
if err != nil {
return nil, err
}
return &SyncHandler{
localDB: localDB,
syncService: syncService,
mariaDB: mariaDB,
}
tmpl: tmpl,
}, nil
}
// SyncStatusResponse represents the sync status
@@ -270,3 +281,24 @@ func (h *SyncHandler) GetPendingChanges(c *gin.Context) {
"changes": changes,
})
}
// SyncStatusPartial renders the sync status partial for htmx
// GET /partials/sync-status
func (h *SyncHandler) SyncStatusPartial(c *gin.Context) {
// Check online status
isOffline, _ := c.Get("is_offline")
// Get pending count
pendingCount := h.localDB.GetPendingCount()
data := gin.H{
"IsOffline": isOffline.(bool),
"PendingCount": pendingCount,
}
c.Header("Content-Type", "text/html; charset=utf-8")
if err := h.tmpl.ExecuteTemplate(c.Writer, "sync_status", data); err != nil {
slog.Error("failed to render sync_status template", "error", err)
c.String(http.StatusInternalServerError, "Template error")
}
}