feat: briefing auto-generate, pages overhaul (dashboard/screener/report/routines), delete routine

This commit is contained in:
asepharyana
2026-09-15 16:39:27 +07:00
parent ff156112e7
commit 94a7f1dfd9
6 changed files with 244 additions and 130 deletions
+18 -2
View File
@@ -4,6 +4,7 @@ import (
"encoding/json"
"net/http"
"strconv"
"time"
"github.com/go-chi/chi/v5"
@@ -11,6 +12,9 @@ import (
"flowsight/internal/store"
)
// todayUTC returns today's date (UTC) for briefing persistence.
func todayUTC() string { return time.Now().UTC().Format("2006-01-02") }
// ListRoutines serves GET /api/routines with last-run status.
func (s *Server) ListRoutines(w http.ResponseWriter, r *http.Request) {
rows, err := s.DB.ListRoutines(s.userKey(r))
@@ -108,11 +112,23 @@ func (s *Server) RunHistory(w http.ResponseWriter, r *http.Request) {
}
// BriefingToday serves GET /api/briefing/today: latest payload + citations.
// When no briefing exists yet it generates one on demand from current
// snapshots (so the UI never shows an empty page), then persists it.
func (s *Server) BriefingToday(w http.ResponseWriter, r *http.Request) {
date, payload, cites, err := s.DB.LatestBriefing()
if err != nil {
writeErr(w, http.StatusNotFound, "no briefing yet — run the morning-briefing routine")
return
uk := s.userKey(r)
p, cc, gerr := s.Engine.BriefingFor(r.Context(), uk)
if gerr != nil {
writeErr(w, http.StatusNotFound, "no briefing yet — subscribe to the morning-briefing routine")
return
}
ccJSON, _ := json.Marshal(cc)
if err := s.DB.SaveBriefing(todayUTC(), p, string(ccJSON)); err != nil {
writeErr(w, http.StatusBadGateway, "db: "+err.Error())
return
}
date, payload, cites = todayUTC(), p, string(ccJSON)
}
writeJSON(w, http.StatusOK, map[string]any{
"date": date, "payload": payload, "citations": cites,
+10
View File
@@ -146,6 +146,16 @@ func (e *Engine) deliver(ctx context.Context, r store.Routine, payload string, c
return e.Notifier.SendTo(ctx, e.UserKey, f, string(cc))
}
// BriefingFor runs the morning briefing scoped to owner (watchlist +
// delivery) without needing a routines row.
func (e *Engine) BriefingFor(ctx context.Context, owner string) (string, []model.Citation, error) {
scoped := *e
if owner != "" {
scoped.UserKey = owner
}
return scoped.Briefing(ctx)
}
// tickers returns the user's watchlist (never empty: falls back to demo set).
func (e *Engine) tickers() []string {
wl, err := e.DB.Watchlist(e.UserKey)