package main import ( _ "embed" "log" "net/http" "strconv" "github.com/kami/maven/internal/ipc" ) //go:embed notifications.html var notificationsHTML string var notificationsTmpl = parsePage("notifications", notificationsHTML, nil) // deliveryRow is one outbox line, with every timestamp already formatted so // the template holds no date logic — same shape as taskRow. type deliveryRow struct { Kind string Target string Channel string Status string Created string Completed string } func deliveryRows(as []ipc.DeliveryAttempt) []deliveryRow { out := make([]deliveryRow, 0, len(as)) for _, a := range as { target := a.Rule if target == "" && a.ReminderID != 0 { target = "reminder #" + strconv.FormatInt(a.ReminderID, 10) } row := deliveryRow{ Kind: a.Kind, Target: target, Channel: a.Channel, Status: a.Status, Created: a.Created.Format("02.01 15:04"), } if a.Completed != nil { row.Completed = a.Completed.Format("15:04") } out = append(out, row) } return out } func handleNotifications(w http.ResponseWriter, r *http.Request, core ipc.CoreAPI) { if !requireCore(w, core, "notifications") { return } ctx := r.Context() nudges, err := core.RecentNudges(ctx, 50) if err != nil { log.Printf("notifications: %v", err) http.Error(w, "notifications error: "+err.Error(), http.StatusBadGateway) return } // The outbox, on the page that already answers "what did she send". // A failed or dropped attempt is why she went quiet, and until now it was // recorded and unreadable (Vikunja #390). Filter with ?status=dropped. status := r.URL.Query().Get("status") attempts, err := core.DeliveryAttempts(ctx, status, 50) if err != nil { // The nudge list is still worth showing, so this is a note on the page // rather than a dead page. log.Printf("notifications: delivery attempts: %v", err) } renderPage(w, notificationsTmpl, map[string]any{ "Nudges": nudges, "Attempts": deliveryRows(attempts), "Status": status, }) }