package handler import ( "net/http" "spend-sparrow/handler/middleware" "spend-sparrow/log" "spend-sparrow/service" t "spend-sparrow/template/transaction" "spend-sparrow/types" "spend-sparrow/utils" "github.com/a-h/templ" "github.com/google/uuid" ) type Transaction interface { Handle(router *http.ServeMux) } type TransactionImpl struct { s service.Transaction account service.Account treasureChest service.TreasureChest a service.Auth r *Render } func NewTransaction(s service.Transaction, account service.Account, treasureChest service.TreasureChest, a service.Auth, r *Render) Transaction { return TransactionImpl{ s: s, account: account, treasureChest: treasureChest, a: a, r: r, } } func (h TransactionImpl) Handle(r *http.ServeMux) { r.Handle("GET /transaction", h.handleTransactionPage()) r.Handle("GET /transaction/{id}", h.handleTransactionItemComp()) r.Handle("POST /transaction/{id}", h.handleUpdateTransaction()) r.Handle("POST /transaction/recalculate", h.handleRecalculate()) r.Handle("DELETE /transaction/{id}", h.handleDeleteTransaction()) } func (h TransactionImpl) handleRecalculate() http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { user := middleware.GetUser(r) if user == nil { utils.DoRedirect(w, r, "/auth/signin") return } err := h.s.RecalculateBalances(user) if err != nil { handleError(w, r, err) return } utils.TriggerToastWithStatus(w, r, "success", "Balances recalculated", http.StatusOK) } } func (h TransactionImpl) handleTransactionPage() http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { user := middleware.GetUser(r) if user == nil { utils.DoRedirect(w, r, "/auth/signin") return } transactions, err := h.s.GetAll(user) if err != nil { handleError(w, r, err) return } accounts, err := h.account.GetAll(user) if err != nil { handleError(w, r, err) return } treasureChests, err := h.treasureChest.GetAll(user) if err != nil { handleError(w, r, err) return } accountMap, treasureChestMap := h.getTransactionData(accounts, treasureChests) comp := t.Transaction(transactions, accountMap, treasureChestMap) h.r.RenderLayout(r, w, comp, user) } } func (h TransactionImpl) handleTransactionItemComp() http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { user := middleware.GetUser(r) if user == nil { utils.DoRedirect(w, r, "/auth/signin") return } accounts, err := h.account.GetAll(user) if err != nil { handleError(w, r, err) return } treasureChests, err := h.treasureChest.GetAll(user) if err != nil { handleError(w, r, err) return } id := r.PathValue("id") if id == "new" { comp := t.EditTransaction(nil, accounts, treasureChests) h.r.Render(r, w, comp) return } transaction, err := h.s.Get(user, id) if err != nil { handleError(w, r, err) return } var comp templ.Component if r.URL.Query().Get("edit") == "true" { comp = t.EditTransaction(transaction, accounts, treasureChests) } else { accountMap, treasureChestMap := h.getTransactionData(accounts, treasureChests) comp = t.TransactionItem(transaction, accountMap, treasureChestMap) } h.r.Render(r, w, comp) } } func (h TransactionImpl) handleUpdateTransaction() http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { user := middleware.GetUser(r) if user == nil { utils.DoRedirect(w, r, "/auth/signin") return } var ( transaction *types.Transaction err error ) input := types.TransactionInput{ Id: r.PathValue("id"), AccountId: r.FormValue("account-id"), TreasureChestId: r.FormValue("treasure-chest-id"), Value: r.FormValue("value"), Timestamp: r.FormValue("timestamp"), TimezoneOffsetMinutes: r.FormValue("timezone-offset"), Note: r.FormValue("note"), } if input.Id == "new" { transaction, err = h.s.Add(user, input) if err != nil { handleError(w, r, err) return } } else { transaction, err = h.s.Update(user, input) if err != nil { handleError(w, r, err) return } } accounts, err := h.account.GetAll(user) if err != nil { handleError(w, r, err) return } treasureChests, err := h.treasureChest.GetAll(user) if err != nil { handleError(w, r, err) return } accountMap, treasureChestMap := h.getTransactionData(accounts, treasureChests) comp := t.TransactionItem(transaction, accountMap, treasureChestMap) h.r.Render(r, w, comp) } } func (h TransactionImpl) handleDeleteTransaction() http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { user := middleware.GetUser(r) if user == nil { utils.DoRedirect(w, r, "/auth/signin") return } id := r.PathValue("id") err := h.s.Delete(user, id) if err != nil { handleError(w, r, err) return } } } func (h TransactionImpl) getTransactionData(accounts []*types.Account, treasureChests []*types.TreasureChest) (map[uuid.UUID]string, map[uuid.UUID]string) { accountMap := make(map[uuid.UUID]string, 0) for _, account := range accounts { accountMap[account.Id] = account.Name } treasureChestMap := make(map[uuid.UUID]string, 0) root := "" for _, treasureChest := range treasureChests { if treasureChest.ParentId == uuid.Nil { root = treasureChest.Name + " > " treasureChestMap[treasureChest.Id] = treasureChest.Name } else { treasureChestMap[treasureChest.Id] = root + treasureChest.Name } } log.Info("treasureChestMap: %v", treasureChestMap) return accountMap, treasureChestMap }