package handler import ( "fmt" "log/slog" "net/http" "spend-sparrow/internal/handler/middleware" "spend-sparrow/internal/service" "spend-sparrow/internal/template/dashboard" "spend-sparrow/internal/utils" "time" ) type Dashboard interface { Handle(router *http.ServeMux) } type DashboardImpl struct { r *Render d *service.Dashboard } func NewDashboard(r *Render, d *service.Dashboard) Dashboard { return DashboardImpl{ r: r, d: d, } } func (handler DashboardImpl) Handle(router *http.ServeMux) { router.Handle("GET /dashboard", handler.handleDashboard()) router.Handle("GET /dashboard/main-chart", handler.handleDashboardMainChart()) router.Handle("GET /dashboard/treasure-chests", handler.handleDashboardTreasureChests()) } func (handler DashboardImpl) handleDashboard() http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { updateSpan(r) user := middleware.GetUser(r) if user == nil { utils.DoRedirect(w, r, "/auth/signin") return } comp := dashboard.Dashboard() handler.r.RenderLayoutWithStatus(r, w, comp, user, http.StatusOK) } } func (handler DashboardImpl) handleDashboardMainChart() http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { updateSpan(r) user := middleware.GetUser(r) series, err := handler.d.MainChart(r.Context(), user) if err != nil { handleError(w, r, err) return } w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusOK) account := "" savings := "" for _, entry := range series { account += fmt.Sprintf(`["%s",%.2f],`, entry.Day.Format(time.RFC3339), float64(entry.Value)/100) savings += fmt.Sprintf(`["%s",%.2f],`, entry.Day.Format(time.RFC3339), float64(entry.Savings)/100) } account = account[:len(account)-1] savings = savings[:len(savings)-1] _, err = fmt.Fprintf(w, ` { "tooltip": { "trigger": "axis", "formatter": "" }, "xAxis": { "type": "time" }, "yAxis": { "axisLabel": { "formatter": "{value} €" } }, "series": [ { "data": [%s], "type": "line", "name": "Account Value" }, { "data": [%s], "type": "line", "name": "Savings" } ] } `, account, savings) if err != nil { slog.InfoContext(r.Context(), "could not write response", "err", err) } } } func (handler DashboardImpl) handleDashboardTreasureChests() http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { updateSpan(r) user := middleware.GetUser(r) treeList, err := handler.d.TreasureChests(r.Context(), user) if err != nil { handleError(w, r, err) return } w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusOK) data := "" for _, item := range treeList { children := "" for _, child := range item.Children { if child.Value < 0 { children += fmt.Sprintf(`{"name":"%s\n%.2f €","value":%d},`, child.Name, float64(child.Value)/100, -child.Value) } else { children += fmt.Sprintf(`{"name":"%s\n%.2f €","value":%d},`, child.Name, float64(child.Value)/100, child.Value) } } children = children[:len(children)-1] data += fmt.Sprintf(`{"name":"%s","children":[%s]},`, item.Name, children) } data = data[:len(data)-1] _, err = fmt.Fprintf(w, ` { "aria": { "enabled": true, "decal": { "show": false } }, "series": [ { "data": [%s], "type": "treemap", "name": "Savings" } ] } `, data) if err != nil { slog.InfoContext(r.Context(), "could not write response", "err", err) } } }