diff --git a/internal/handler/home.go b/internal/handler/home.go index 431ddb9..296b45d 100644 --- a/internal/handler/home.go +++ b/internal/handler/home.go @@ -21,7 +21,7 @@ func (h *homeHandler) HomePage(w http.ResponseWriter, r *http.Request) { http.Redirect(w, r, "/auth", http.StatusSeeOther) return } - http.Redirect(w, r, "/app/spaces", http.StatusSeeOther) + http.Redirect(w, r, "/app/home", http.StatusSeeOther) } func (h *homeHandler) PrivacyPage(w http.ResponseWriter, r *http.Request) { diff --git a/internal/handler/redirect.go b/internal/handler/redirect.go index 7c47547..d391463 100644 --- a/internal/handler/redirect.go +++ b/internal/handler/redirect.go @@ -9,5 +9,5 @@ func NewRedirectHandler() *redirectHandler { } func (h *redirectHandler) Spaces(w http.ResponseWriter, r *http.Request) { - http.Redirect(w, r, "/app/spaces", http.StatusMovedPermanently) + http.Redirect(w, r, "/app/home", http.StatusMovedPermanently) } diff --git a/internal/handler/redirect_test.go b/internal/handler/redirect_test.go index c1a5d0a..297bd2c 100644 --- a/internal/handler/redirect_test.go +++ b/internal/handler/redirect_test.go @@ -17,5 +17,5 @@ func TestRedirectHandler_RederictToSpaces(t *testing.T) { h.Spaces(w, req) assert.Equal(t, http.StatusMovedPermanently, w.Code) - assert.Equal(t, "/app/spaces", w.Header().Get("Location")) + assert.Equal(t, "/app/home", w.Header().Get("Location")) } diff --git a/internal/handler/space.go b/internal/handler/space.go index 192ca67..e71e401 100644 --- a/internal/handler/space.go +++ b/internal/handler/space.go @@ -41,6 +41,42 @@ func NewSpaceHandler( } } +func (h *spaceHandler) HomePage(w http.ResponseWriter, r *http.Request) { + user := ctxkeys.User(r.Context()) + if user == nil { + ui.RenderError(w, r, "Unauthorized", http.StatusUnauthorized) + return + } + + owned, err := h.spaceService.GetOwnedSpaces(user.ID) + if err != nil { + slog.Error("failed to load owned spaces", "error", err, "user_id", user.ID) + ui.RenderError(w, r, "Failed to load spaces", http.StatusInternalServerError) + return + } + + shared, err := h.spaceService.GetSharedSpaces(user.ID) + if err != nil { + slog.Error("failed to load shared spaces", "error", err, "user_id", user.ID) + ui.RenderError(w, r, "Failed to load spaces", http.StatusInternalServerError) + return + } + + ownedCards := h.buildSpaceCards(owned) + sharedCards := h.buildSpaceCards(shared) + + total := decimal.Zero + for _, c := range ownedCards { + total = total.Add(c.TotalBalance) + } + + ui.Render(w, r, pages.Home(pages.HomeProps{ + OwnedSpaces: ownedCards, + SharedSpaces: sharedCards, + TotalBalance: total, + })) +} + func (h *spaceHandler) SpacesPage(w http.ResponseWriter, r *http.Request) { user := ctxkeys.User(r.Context()) if user == nil { diff --git a/internal/routes/routes.go b/internal/routes/routes.go index a02fccd..0b90909 100644 --- a/internal/routes/routes.go +++ b/internal/routes/routes.go @@ -85,6 +85,8 @@ func SetupRoutes(a *app.App) http.Handler { r.Group("/app", func(g *router.Group) { g.Use(middleware.RequireAuth) + g.Get("/home", spaceH.HomePage).Name("page.app.home") + g.SubGroup("/spaces", func(g *router.Group) { g.Get("", spaceH.SpacesPage).Name("page.app.spaces") g.Get("/create", spaceH.CreateSpacePage).Name("page.app.spaces.create") diff --git a/internal/ui/pages/home.templ b/internal/ui/pages/home.templ new file mode 100644 index 0000000..54ec359 --- /dev/null +++ b/internal/ui/pages/home.templ @@ -0,0 +1,91 @@ +package pages + +import ( + "fmt" + + "git.juancwu.dev/juancwu/budgit/internal/ctxkeys" + "git.juancwu.dev/juancwu/budgit/internal/routeurl" + "git.juancwu.dev/juancwu/budgit/internal/ui/blocks" + "git.juancwu.dev/juancwu/budgit/internal/ui/components/button" + "git.juancwu.dev/juancwu/budgit/internal/ui/components/icon" + "git.juancwu.dev/juancwu/budgit/internal/ui/layouts" + "git.juancwu.dev/juancwu/budgit/internal/ui/utils" + "github.com/shopspring/decimal" +) + +type HomeProps struct { + OwnedSpaces []blocks.SpaceCardInfo + SharedSpaces []blocks.SpaceCardInfo + TotalBalance decimal.Decimal +} + +templ Home(props HomeProps) { + {{ + user := ctxkeys.User(ctx) + displayName := "" + if user != nil && user.Name != nil { + displayName = *user.Name + } + totalSpaces := len(props.OwnedSpaces) + len(props.SharedSpaces) + }} + @layouts.App("Home", spaceOverviewSidebarContent()) { +
Home
+Create a space to start tracking your expenses.
+ } else { + {{ + countPhrase := fmt.Sprintf("Across %d spaces", totalSpaces) + if totalSpaces == 1 { + countPhrase = "Across 1 space" + } + }} +{ countPhrase } you own you have ${ utils.FormatDecimalWithThousands(props.TotalBalance.StringFixedBank(2)) } tracked.
+ } +You don't own any spaces yet.
+ } else { +No spaces have been shared with you.
+ } else { +