feat: add transaction from overview page
This commit is contained in:
parent
0008e93973
commit
a17703d30d
4 changed files with 61 additions and 11 deletions
|
|
@ -179,6 +179,27 @@ func (h *SpaceHandler) OverviewPage(w http.ResponseWriter, r *http.Request) {
|
||||||
slog.Error("failed to build list cards", "error", err, "space_id", spaceID)
|
slog.Error("failed to build list cards", "error", err, "space_id", spaceID)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
tags, err := h.tagService.GetTagsForSpace(spaceID)
|
||||||
|
if err != nil {
|
||||||
|
slog.Error("failed to get tags for space", "error", err, "space_id", spaceID)
|
||||||
|
http.Error(w, "Internal Server Error", http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
listsWithItems, err := h.listService.GetListsWithUncheckedItems(spaceID)
|
||||||
|
if err != nil {
|
||||||
|
slog.Error("failed to get lists with unchecked items", "error", err, "space_id", spaceID)
|
||||||
|
http.Error(w, "Internal Server Error", http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
methods, err := h.methodService.GetMethodsForSpace(spaceID)
|
||||||
|
if err != nil {
|
||||||
|
slog.Error("failed to get payment methods", "error", err, "space_id", spaceID)
|
||||||
|
http.Error(w, "Internal Server Error", http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
ui.Render(w, r, pages.SpaceOverviewPage(pages.OverviewData{
|
ui.Render(w, r, pages.SpaceOverviewPage(pages.OverviewData{
|
||||||
Space: space,
|
Space: space,
|
||||||
Balance: balance,
|
Balance: balance,
|
||||||
|
|
@ -187,6 +208,9 @@ func (h *SpaceHandler) OverviewPage(w http.ResponseWriter, r *http.Request) {
|
||||||
Budgets: budgets,
|
Budgets: budgets,
|
||||||
UpcomingRecurring: recs,
|
UpcomingRecurring: recs,
|
||||||
ShoppingLists: cards,
|
ShoppingLists: cards,
|
||||||
|
Tags: tags,
|
||||||
|
Methods: methods,
|
||||||
|
ListsWithItems: listsWithItems,
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -713,6 +737,13 @@ func (h *SpaceHandler) CreateExpense(w http.ResponseWriter, r *http.Request) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If a redirect URL was provided (e.g. from the overview page), redirect instead of inline swap
|
||||||
|
if redirectURL := r.FormValue("redirect"); redirectURL != "" {
|
||||||
|
w.Header().Set("HX-Redirect", redirectURL)
|
||||||
|
w.WriteHeader(http.StatusOK)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
balance, err := h.expenseService.GetBalanceForSpace(spaceID)
|
balance, err := h.expenseService.GetBalanceForSpace(spaceID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
slog.Error("failed to get balance", "error", err, "space_id", spaceID)
|
slog.Error("failed to get balance", "error", err, "space_id", spaceID)
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,12 @@
|
||||||
package dialogs
|
package dialogs
|
||||||
|
|
||||||
import "git.juancwu.dev/juancwu/budgit/internal/ui/components/dialog"
|
import (
|
||||||
import "git.juancwu.dev/juancwu/budgit/internal/ui/components/button"
|
"fmt"
|
||||||
import "git.juancwu.dev/juancwu/budgit/internal/ui/components/expense"
|
"git.juancwu.dev/juancwu/budgit/internal/ui/components/dialog"
|
||||||
import "git.juancwu.dev/juancwu/budgit/internal/model"
|
"git.juancwu.dev/juancwu/budgit/internal/ui/components/button"
|
||||||
|
"git.juancwu.dev/juancwu/budgit/internal/ui/components/expense"
|
||||||
|
"git.juancwu.dev/juancwu/budgit/internal/model"
|
||||||
|
)
|
||||||
|
|
||||||
templ AddTransaction(space *model.Space, tags []*model.Tag, listsWithItems []model.ListWithUncheckedItems, methods []*model.PaymentMethod) {
|
templ AddTransaction(space *model.Space, tags []*model.Tag, listsWithItems []model.ListWithUncheckedItems, methods []*model.PaymentMethod) {
|
||||||
@dialog.Dialog(dialog.Props{ID: "add-transaction-dialog"}) {
|
@dialog.Dialog(dialog.Props{ID: "add-transaction-dialog"}) {
|
||||||
|
|
@ -27,6 +30,7 @@ templ AddTransaction(space *model.Space, tags []*model.Tag, listsWithItems []mod
|
||||||
ListsWithItems: listsWithItems,
|
ListsWithItems: listsWithItems,
|
||||||
PaymentMethods: methods,
|
PaymentMethods: methods,
|
||||||
DialogID: "add-transaction-dialog",
|
DialogID: "add-transaction-dialog",
|
||||||
|
RedirectURL: fmt.Sprintf("/app/spaces/%s/expenses?created=true", space.ID),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -22,16 +22,21 @@ type AddExpenseFormProps struct {
|
||||||
ListsWithItems []model.ListWithUncheckedItems
|
ListsWithItems []model.ListWithUncheckedItems
|
||||||
PaymentMethods []*model.PaymentMethod
|
PaymentMethods []*model.PaymentMethod
|
||||||
DialogID string // which dialog to close on success
|
DialogID string // which dialog to close on success
|
||||||
|
RedirectURL string // when set, server returns HX-Redirect instead of inline swap
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p AddExpenseFormProps) formAttrs() templ.Attributes {
|
func (p AddExpenseFormProps) formAttrs() templ.Attributes {
|
||||||
closeScript := "on htmx:afterOnLoad if event.detail.xhr.status == 200 then call window.tui.dialog.close('" + p.DialogID + "') then reset() me then show #item-selector-section end"
|
attrs := templ.Attributes{
|
||||||
return templ.Attributes{
|
|
||||||
"hx-post": "/app/spaces/" + p.Space.ID + "/expenses",
|
"hx-post": "/app/spaces/" + p.Space.ID + "/expenses",
|
||||||
"hx-target": "#expenses-list-wrapper",
|
|
||||||
"hx-swap": "innerHTML",
|
|
||||||
"_": closeScript,
|
|
||||||
}
|
}
|
||||||
|
if p.RedirectURL != "" {
|
||||||
|
attrs["_"] = "on htmx:afterOnLoad if event.detail.xhr.status == 200 then call window.tui.dialog.close('" + p.DialogID + "') end"
|
||||||
|
} else {
|
||||||
|
attrs["hx-target"] = "#expenses-list-wrapper"
|
||||||
|
attrs["hx-swap"] = "innerHTML"
|
||||||
|
attrs["_"] = "on htmx:afterOnLoad if event.detail.xhr.status == 200 then call window.tui.dialog.close('" + p.DialogID + "') then reset() me then show #item-selector-section end"
|
||||||
|
}
|
||||||
|
return attrs
|
||||||
}
|
}
|
||||||
|
|
||||||
templ AddExpenseForm(props AddExpenseFormProps) {
|
templ AddExpenseForm(props AddExpenseFormProps) {
|
||||||
|
|
@ -40,6 +45,9 @@ templ AddExpenseForm(props AddExpenseFormProps) {
|
||||||
{ props.formAttrs()... }
|
{ props.formAttrs()... }
|
||||||
>
|
>
|
||||||
@csrf.Token()
|
@csrf.Token()
|
||||||
|
if props.RedirectURL != "" {
|
||||||
|
<input type="hidden" name="redirect" value={ props.RedirectURL }/>
|
||||||
|
}
|
||||||
// Type
|
// Type
|
||||||
<div class="flex gap-4">
|
<div class="flex gap-4">
|
||||||
<div class="flex items-start gap-3">
|
<div class="flex items-start gap-3">
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,7 @@ import (
|
||||||
"git.juancwu.dev/juancwu/budgit/internal/ui/components/chart"
|
"git.juancwu.dev/juancwu/budgit/internal/ui/components/chart"
|
||||||
"git.juancwu.dev/juancwu/budgit/internal/ui/components/icon"
|
"git.juancwu.dev/juancwu/budgit/internal/ui/components/icon"
|
||||||
"git.juancwu.dev/juancwu/budgit/internal/ui/layouts"
|
"git.juancwu.dev/juancwu/budgit/internal/ui/layouts"
|
||||||
|
"git.juancwu.dev/juancwu/budgit/internal/ui/blocks/dialogs"
|
||||||
)
|
)
|
||||||
|
|
||||||
type OverviewData struct {
|
type OverviewData struct {
|
||||||
|
|
@ -19,6 +20,9 @@ type OverviewData struct {
|
||||||
Budgets []*model.BudgetWithSpent
|
Budgets []*model.BudgetWithSpent
|
||||||
UpcomingRecurring []*model.RecurringExpenseWithTagsAndMethod
|
UpcomingRecurring []*model.RecurringExpenseWithTagsAndMethod
|
||||||
ShoppingLists []model.ListCardData
|
ShoppingLists []model.ListCardData
|
||||||
|
Tags []*model.Tag
|
||||||
|
Methods []*model.PaymentMethod
|
||||||
|
ListsWithItems []model.ListWithUncheckedItems
|
||||||
}
|
}
|
||||||
|
|
||||||
func overviewProgressBarColor(status model.BudgetStatus) string {
|
func overviewProgressBarColor(status model.BudgetStatus) string {
|
||||||
|
|
@ -135,7 +139,10 @@ templ overviewSectionHeader(title, href string) {
|
||||||
|
|
||||||
templ overviewBalanceCard(data OverviewData) {
|
templ overviewBalanceCard(data OverviewData) {
|
||||||
<div class="border rounded-lg p-4 bg-card text-card-foreground">
|
<div class="border rounded-lg p-4 bg-card text-card-foreground">
|
||||||
|
<div class="flex items-center justify-between">
|
||||||
<h3 class="font-semibold mb-3">Current Balance</h3>
|
<h3 class="font-semibold mb-3">Current Balance</h3>
|
||||||
|
@dialogs.AddTransaction(data.Space, data.Tags, data.ListsWithItems, data.Methods)
|
||||||
|
</div>
|
||||||
<p class={ "text-3xl font-bold", templ.KV("text-destructive", data.Balance < 0) }>
|
<p class={ "text-3xl font-bold", templ.KV("text-destructive", data.Balance < 0) }>
|
||||||
{ fmt.Sprintf("$%.2f", float64(data.Balance)/100.0) }
|
{ fmt.Sprintf("$%.2f", float64(data.Balance)/100.0) }
|
||||||
</p>
|
</p>
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue