diff --git a/cmd/server/main.go b/cmd/server/main.go index a4cb7be..7f8ca87 100644 --- a/cmd/server/main.go +++ b/cmd/server/main.go @@ -625,6 +625,7 @@ func setupRouter(db *gorm.DB, cfg *config.Config, local *localdb.LocalDB, dbUser syncAPI := api.Group("/sync") { syncAPI.GET("/status", syncHandler.GetStatus) + syncAPI.GET("/info", syncHandler.GetInfo) syncAPI.POST("/components", syncHandler.SyncComponents) syncAPI.POST("/pricelists", syncHandler.SyncPricelists) syncAPI.POST("/all", syncHandler.SyncAll) diff --git a/internal/handlers/sync.go b/internal/handlers/sync.go index bbea2ef..d5937aa 100644 --- a/internal/handlers/sync.go +++ b/internal/handlers/sync.go @@ -282,6 +282,70 @@ func (h *SyncHandler) GetPendingChanges(c *gin.Context) { }) } +// SyncInfoResponse represents sync information +type SyncInfoResponse struct { + LastSyncAt *time.Time `json:"last_sync_at"` + IsOnline bool `json:"is_online"` + ErrorCount int `json:"error_count"` + Errors []SyncError `json:"errors,omitempty"` +} + +// SyncError represents a sync error +type SyncError struct { + Timestamp time.Time `json:"timestamp"` + Message string `json:"message"` +} + +// GetInfo returns sync information for modal +// GET /api/sync/info +func (h *SyncHandler) GetInfo(c *gin.Context) { + // Check online status by pinging MariaDB + isOnline := h.checkOnline() + + // Get sync times + lastPricelistSync := h.localDB.GetLastSyncTime() + + // Get error count (only changes with LastError != "") + errorCount := int(h.localDB.CountErroredChanges()) + + // Get recent errors (last 10) + changes, err := h.localDB.GetPendingChanges() + if err != nil { + slog.Error("failed to get pending changes for sync info", "error", err) + // Even if we can't get changes, we can still return the error count + c.JSON(http.StatusOK, SyncInfoResponse{ + LastSyncAt: lastPricelistSync, + IsOnline: isOnline, + ErrorCount: errorCount, + Errors: []SyncError{}, // Return empty errors list + }) + return + } + + var errors []SyncError + for _, change := range changes { + // Check if there's a last error and it's not empty + if change.LastError != "" { + errors = append(errors, SyncError{ + Timestamp: change.CreatedAt, + Message: change.LastError, + }) + } + } + + // Limit to last 10 errors + if len(errors) > 10 { + errors = errors[:10] + } + + c.JSON(http.StatusOK, SyncInfoResponse{ + LastSyncAt: lastPricelistSync, + IsOnline: isOnline, + ErrorCount: errorCount, + Errors: errors, + }) +} + // SyncStatusPartial renders the sync status partial for htmx // GET /partials/sync-status func (h *SyncHandler) SyncStatusPartial(c *gin.Context) { diff --git a/internal/localdb/localdb.go b/internal/localdb/localdb.go index e609692..9558e38 100644 --- a/internal/localdb/localdb.go +++ b/internal/localdb/localdb.go @@ -396,6 +396,13 @@ func (l *LocalDB) CountPendingChangesByType(entityType string) int64 { return count } +// CountErroredChanges returns the number of pending changes with errors +func (l *LocalDB) CountErroredChanges() int64 { + var count int64 + l.db.Model(&PendingChange{}).Where("last_error != ?", "").Count(&count) + return count +} + // MarkChangesSynced marks multiple pending changes as synced by deleting them func (l *LocalDB) MarkChangesSynced(ids []int64) error { if len(ids) == 0 { diff --git a/web/templates/base.html b/web/templates/base.html index e5d2024..1cf1124 100644 --- a/web/templates/base.html +++ b/web/templates/base.html @@ -44,6 +44,52 @@
+ + +