feat: new home page
This commit is contained in:
parent
59c28aa2e8
commit
145eed9eef
7 changed files with 142 additions and 3 deletions
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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"))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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")
|
||||
|
|
|
|||
91
internal/ui/pages/home.templ
Normal file
91
internal/ui/pages/home.templ
Normal file
|
|
@ -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()) {
|
||||
<div class="container px-6 py-8 mx-auto space-y-10">
|
||||
<div class="w-full space-y-2 md:space-y-0 md:flex justify-between items-end">
|
||||
<div class="space-y-2">
|
||||
<p class="text-muted-foreground text-sm">Home</p>
|
||||
<h1 class="text-2xl font-bold">Hello, { displayName }</h1>
|
||||
if totalSpaces == 0 {
|
||||
<p class="text-muted-foreground">Create a space to start tracking your expenses.</p>
|
||||
} else {
|
||||
{{
|
||||
countPhrase := fmt.Sprintf("Across %d spaces", totalSpaces)
|
||||
if totalSpaces == 1 {
|
||||
countPhrase = "Across 1 space"
|
||||
}
|
||||
}}
|
||||
<p class="text-muted-foreground">{ countPhrase } you own you have <span class="font-medium text-foreground">${ utils.FormatDecimalWithThousands(props.TotalBalance.StringFixedBank(2)) }</span> tracked.</p>
|
||||
}
|
||||
</div>
|
||||
<div>
|
||||
@button.Button(button.Props{
|
||||
Class: "flex gap-2 items-center",
|
||||
Href: routeurl.URL("page.app.spaces.create"),
|
||||
}) {
|
||||
@icon.Plus()
|
||||
Create space
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
<section class="space-y-3">
|
||||
<div class="flex items-center justify-between">
|
||||
<h2 class="text-lg font-semibold">My spaces</h2>
|
||||
<span class="text-xs text-muted-foreground">{ fmt.Sprintf("%d", len(props.OwnedSpaces)) }</span>
|
||||
</div>
|
||||
if len(props.OwnedSpaces) == 0 {
|
||||
<p class="text-sm text-muted-foreground">You don't own any spaces yet.</p>
|
||||
} else {
|
||||
<div>
|
||||
for _, space := range props.OwnedSpaces {
|
||||
@blocks.SpaceCard(space)
|
||||
}
|
||||
</div>
|
||||
}
|
||||
</section>
|
||||
<section class="space-y-3">
|
||||
<div class="flex items-center justify-between">
|
||||
<h2 class="text-lg font-semibold">Shared with me</h2>
|
||||
<span class="text-xs text-muted-foreground">{ fmt.Sprintf("%d", len(props.SharedSpaces)) }</span>
|
||||
</div>
|
||||
if len(props.SharedSpaces) == 0 {
|
||||
<p class="text-sm text-muted-foreground">No spaces have been shared with you.</p>
|
||||
} else {
|
||||
<div>
|
||||
for _, space := range props.SharedSpaces {
|
||||
@blocks.SpaceCard(space)
|
||||
}
|
||||
</div>
|
||||
}
|
||||
</section>
|
||||
</div>
|
||||
}
|
||||
}
|
||||
|
|
@ -81,6 +81,16 @@ templ spaceOverviewSidebarContent() {
|
|||
Overview
|
||||
}
|
||||
@sidebar.Menu() {
|
||||
@sidebar.MenuItem() {
|
||||
@sidebar.MenuButton(sidebar.MenuButtonProps{
|
||||
Href: routeurl.URL("page.app.home"),
|
||||
IsActive: ctxkeys.URLPath(ctx) == routeurl.URL("page.app.home"),
|
||||
Tooltip: "Home",
|
||||
}) {
|
||||
@icon.House()
|
||||
<span>Home</span>
|
||||
}
|
||||
}
|
||||
@sidebar.MenuItem() {
|
||||
@sidebar.MenuButton(sidebar.MenuButtonProps{
|
||||
Href: routeurl.URL("page.app.spaces"),
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue