feat(dashboard): #192 include treemap of treasure chests
All checks were successful
Build Docker Image / Build-Docker-Image (push) Successful in 4m47s
Build and Push Docker Image / Build-And-Push-Docker-Image (push) Successful in 3m36s

This commit was merged in pull request #195.
This commit is contained in:
2025-06-20 21:28:47 +02:00
parent 3120c19669
commit 72869e5c68
6 changed files with 138 additions and 14 deletions

View File

@@ -29,7 +29,8 @@ func NewDashboard(r *Render, d *service.Dashboard) Dashboard {
func (handler DashboardImpl) Handle(router *http.ServeMux) {
router.Handle("GET /dashboard", handler.handleDashboard())
router.Handle("GET /dashboard/dataset", handler.handleDashboardDataset())
router.Handle("GET /dashboard/main-chart", handler.handleDashboardMainChart())
router.Handle("GET /dashboard/treasure-chests", handler.handleDashboardTreasureChests())
}
func (handler DashboardImpl) handleDashboard() http.HandlerFunc {
@@ -47,7 +48,7 @@ func (handler DashboardImpl) handleDashboard() http.HandlerFunc {
}
}
func (handler DashboardImpl) handleDashboardDataset() http.HandlerFunc {
func (handler DashboardImpl) handleDashboardMainChart() http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
updateSpan(r)
@@ -74,6 +75,9 @@ func (handler DashboardImpl) handleDashboardDataset() http.HandlerFunc {
_, err = fmt.Fprintf(w, `
{
"aria": {
"enabled": true
},
"tooltip": {
"trigger": "axis",
"formatter": "<updated by client>"
@@ -105,3 +109,53 @@ func (handler DashboardImpl) handleDashboardDataset() http.HandlerFunc {
}
}
}
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
},
"series": [
{
"data": [%s],
"type": "treemap",
"name": "Savings"
}
]
}
`, data)
if err != nil {
slog.InfoContext(r.Context(), "could not write response", "err", err)
}
}
}

View File

@@ -75,3 +75,40 @@ func (s Dashboard) MainChart(
return timeEntries, nil
}
func (s Dashboard) TreasureChests(
ctx context.Context,
user *types.User,
) ([]*types.DashboardTreasureChest, error) {
if user == nil {
return nil, ErrUnauthorized
}
treasureChests := make([]*types.TreasureChest, 0)
err := s.db.SelectContext(ctx, &treasureChests, `SELECT * FROM treasure_chest WHERE user_id = ?`, user.Id)
err = db.TransformAndLogDbError(ctx, "dashboard TreasureChests", nil, err)
if err != nil {
return nil, err
}
treasureChests = sortTreasureChests(treasureChests)
result := make([]*types.DashboardTreasureChest, 0)
for _, t := range treasureChests {
if t.ParentId == nil {
result = append(result, &types.DashboardTreasureChest{
Name: t.Name,
Value: t.CurrentBalance,
Children: make([]types.DashboardTreasureChest, 0),
})
} else {
result[len(result)-1].Children = append(result[len(result)-1].Children, types.DashboardTreasureChest{
Name: t.Name,
Value: t.CurrentBalance,
Children: make([]types.DashboardTreasureChest, 0),
})
}
}
return result, nil
}

View File

@@ -205,7 +205,7 @@ func (s TreasureChestImpl) GetAll(ctx context.Context, user *types.User) ([]*typ
return nil, err
}
return sortTree(treasureChests), nil
return sortTreasureChests(treasureChests), nil
}
func (s TreasureChestImpl) Delete(ctx context.Context, user *types.User, idStr string) error {
@@ -277,7 +277,7 @@ func (s TreasureChestImpl) Delete(ctx context.Context, user *types.User, idStr s
return nil
}
func sortTree(nodes []*types.TreasureChest) []*types.TreasureChest {
func sortTreasureChests(nodes []*types.TreasureChest) []*types.TreasureChest {
var (
roots []*types.TreasureChest
)

View File

@@ -2,6 +2,7 @@ package dashboard
templ Dashboard() {
<div class="mt-10 h-full">
<div id="graph" class="h-96"></div>
<div id="main-chart" class="h-96"></div>
<div id="treasure-chests" class="h-96"></div>
</div>
}

View File

@@ -22,3 +22,9 @@ type DashboardMainChartEntry struct {
Value int64
Savings int64
}
type DashboardTreasureChest struct {
Name string
Value int64
Children []DashboardTreasureChest
}