Compare commits

..

2 commits

Author SHA1 Message Date
Nickiel12
00a697d1a6 got autofilling working 2024-02-27 19:00:39 -08:00
Nickiel12
9610ddf731 ran gofmt 2024-02-27 18:38:00 -08:00
10 changed files with 267 additions and 216 deletions

2
api.go
View file

@ -41,7 +41,7 @@ func getTransactions(w http.ResponseWriter, req *http.Request) {
transactions := []types.Transaction{} transactions := []types.Transaction{}
err := db.GetTransactions(&transactions, input.ResultCount, input.PageNum); err := db.GetTransactions(&transactions, input.ResultCount, input.PageNum)
if err != nil { if err != nil {

103
db/db.go
View file

@ -1,8 +1,8 @@
package db package db
import ( import (
"database/sql"
"fmt" "fmt"
"database/sql"
"github.com/jmoiron/sqlx" "github.com/jmoiron/sqlx"
_ "github.com/lib/pq" _ "github.com/lib/pq"
@ -11,14 +11,14 @@ import (
"nickiel.net/recount_server/types" "nickiel.net/recount_server/types"
) )
var DB_TYPE string; var DB_TYPE string
var DB_SCHEMA string; var DB_SCHEMA string
var DB_CONNECTION_STRING string; var DB_CONNECTION_STRING string
func SetConstants(db_type string, db_schema string, db_conn_string string) { func SetConstants(db_type string, db_schema string, db_conn_string string) {
DB_TYPE = db_type; DB_TYPE = db_type
DB_SCHEMA = db_schema; DB_SCHEMA = db_schema
DB_CONNECTION_STRING = db_conn_string; DB_CONNECTION_STRING = db_conn_string
} }
func GetTransactions(transactions *[]types.Transaction, resultCount int, pageNum int) error { func GetTransactions(transactions *[]types.Transaction, resultCount int, pageNum int) error {
@ -69,62 +69,61 @@ func NewTransaction(transaction types.Transaction) error {
} }
func GetUserAccounts(userID int) ([]types.Account, error) { func GetUserAccounts(userID int) ([]types.Account, error) {
user_accounts := make([]types.Account, 4); user_accounts := make([]types.Account, 4)
user_accounts[0] = types.Account { user_accounts[0] = types.Account{
Id: 1, Id: 1,
DisplayName: sql.NullString { DisplayName: sql.NullString{
String: "Savings", String: "Savings",
Valid: true, Valid: true,
} , },
Description: sql.NullString { Description: sql.NullString{
String: "BECU Saving Account", String: "BECU Saving Account",
Valid: true, Valid: true,
}, },
}; }
user_accounts[1] = types.Account { user_accounts[1] = types.Account{
Id: 2, Id: 2,
DisplayName: sql.NullString { DisplayName: sql.NullString{
String: "BECU Credit Card", String: "BECU Credit Card",
Valid: true, Valid: true,
} , },
Description: sql.NullString { Description: sql.NullString{
String: "BECU Saving Account", String: "BECU Saving Account",
Valid: true, Valid: true,
}, },
}; }
return user_accounts, nil; return user_accounts, nil
} }
func GetUserBuckets(userID int) ([]types.Bucket, error) { func GetUserBuckets(userID int) ([]types.Bucket, error) {
bucket1 := types.Bucket{ bucket1 := types.Bucket{
Id: 1, Id: 1,
DisplayCode: sql.NullString{String: "SVNG", Valid: true}, DisplayCode: sql.NullString{String: "SVNG", Valid: true},
DisplayName: sql.NullString{String: "Savings", Valid: true}, DisplayName: sql.NullString{String: "Savings", Valid: true},
Description: sql.NullString{String: "The Savings Bucket", Valid: true}, Description: sql.NullString{String: "The Savings Bucket", Valid: true},
} }
bucket2 := types.Bucket{ bucket2 := types.Bucket{
Id: 2, Id: 2,
DisplayCode: sql.NullString{String: "SPND", Valid: true}, DisplayCode: sql.NullString{String: "SPND", Valid: true},
DisplayName: sql.NullString{String: "Spending", Valid: true}, DisplayName: sql.NullString{String: "Spending", Valid: true},
Description: sql.NullString{String: "The Spending Bucket", Valid: true}, Description: sql.NullString{String: "The Spending Bucket", Valid: true},
} }
// Creating a slice of length two and populating it with the two structs // Creating a slice of length two and populating it with the two structs
buckets := []types.Bucket{bucket1, bucket2} buckets := []types.Bucket{bucket1, bucket2}
return buckets, nil; return buckets, nil
} }
func GetTransPaneEntries(userID int) ([]types.QuickTransactionType, error) { func GetTransPaneEntries(userID int) ([]types.QuickTransactionType, error) {
transaction_types := make([]types.QuickTransactionType, 1); transaction_types := make([]types.QuickTransactionType, 1)
transaction_types[0] = types.QuickTransactionType { transaction_types[0] = types.QuickTransactionType{
DisplayName: "Manual", DisplayName: "Manual",
}; }
return transaction_types, nil; return transaction_types, nil
} }

View file

@ -1,8 +1,8 @@
package main package main
import ( import (
"nickiel.net/recount_server/db"
"nickiel.net/recount_server/tests" "nickiel.net/recount_server/tests"
"nickiel.net/recount_server/db"
"nickiel.net/recount_server/web" "nickiel.net/recount_server/web"
"net/http" "net/http"
@ -60,7 +60,7 @@ func main() {
debug_mode.Init_testdb(DB_TYPE, DB_CONNECTION_STRING) debug_mode.Init_testdb(DB_TYPE, DB_CONNECTION_STRING)
} }
db.SetConstants(DB_TYPE, DB_SCHEMA, DB_CONNECTION_STRING); db.SetConstants(DB_TYPE, DB_SCHEMA, DB_CONNECTION_STRING)
debug_mode.SetLogLevel(zerolog.GlobalLevel()) debug_mode.SetLogLevel(zerolog.GlobalLevel())

View file

@ -1,8 +1,8 @@
package debug_mode package debug_mode
import ( import (
"nickiel.net/recount_server/types"
"encoding/json" "encoding/json"
"nickiel.net/recount_server/types"
"os" "os"
"github.com/jmoiron/sqlx" "github.com/jmoiron/sqlx"
@ -11,32 +11,31 @@ import (
"github.com/rs/zerolog/log" "github.com/rs/zerolog/log"
) )
func SetLogLevel(level zerolog.Level) { func SetLogLevel(level zerolog.Level) {
zerolog.SetGlobalLevel(level) zerolog.SetGlobalLevel(level)
} }
func Init_testdb(DB_TYPE string, DB_CONNECTION_STRING string) { func Init_testdb(DB_TYPE string, DB_CONNECTION_STRING string) {
cwd, err := os.Getwd(); cwd, err := os.Getwd()
if err != nil { if err != nil {
log.Fatal().Err(err).Msg("Could not get current working directory") log.Fatal().Err(err).Msg("Could not get current working directory")
} else { } else {
cwd = cwd + "/"; cwd = cwd + "/"
log.Trace().Msgf("Currect working directory is: %s", cwd) log.Trace().Msgf("Currect working directory is: %s", cwd)
} }
_, err = os.Stat(cwd + DB_CONNECTION_STRING) _, err = os.Stat(cwd + DB_CONNECTION_STRING)
if err != nil { if err != nil {
log.Debug().Msg("No existing test.db file found") log.Debug().Msg("No existing test.db file found")
} else { } else {
log.Debug().Msg("Found existing test.db file. Attempting to delete") log.Debug().Msg("Found existing test.db file. Attempting to delete")
err = os.Remove(cwd + DB_CONNECTION_STRING) err = os.Remove(cwd + DB_CONNECTION_STRING)
if err != nil { if err != nil {
log.Warn().Err(err).Msg("Failed to delete testing db. Continueing anyways") log.Warn().Err(err).Msg("Failed to delete testing db. Continueing anyways")
} else { } else {
log.Debug().Msg("Deleted test.db file successfully") log.Debug().Msg("Deleted test.db file successfully")
} }
} }
db, err := sqlx.Connect(DB_TYPE, DB_CONNECTION_STRING) db, err := sqlx.Connect(DB_TYPE, DB_CONNECTION_STRING)

View file

@ -3,7 +3,7 @@ package types
import ( import (
"database/sql" "database/sql"
"time" "time"
) )
type Transaction struct { type Transaction struct {
@ -22,33 +22,33 @@ type Account struct {
} }
type Bucket struct { type Bucket struct {
Id int `db:"bkt_id" json:"id"` Id int `db:"bkt_id" json:"id"`
DisplayCode sql.NullString `db:"bkt_display_code" json:"DisplayCode"` DisplayCode sql.NullString `db:"bkt_display_code" json:"DisplayCode"`
DisplayName sql.NullString `db:"bkt_display_name" json:"DisplayName"` DisplayName sql.NullString `db:"bkt_display_name" json:"DisplayName"`
Description sql.NullString `db:"bkt_description" json:"Description"` Description sql.NullString `db:"bkt_description" json:"Description"`
} }
type HumanLegibleTransaction struct { type HumanLegibleTransaction struct {
Id int `db:"trns_id" json:"Id"` Id int `db:"trns_id" json:"Id"`
Amount string `db:"trns_amount" json:"Amount"` Amount string `db:"trns_amount" json:"Amount"`
Description sql.NullString `db:"trns_description" json:"Description"` Description sql.NullString `db:"trns_description" json:"Description"`
AccountName sql.NullString `db:"account_name" json:"AccountName"` AccountName sql.NullString `db:"account_name" json:"AccountName"`
Account int `db:"trns_account" json:"Account"` Account int `db:"trns_account" json:"Account"`
Bucket sql.NullInt64 `db:"trns_bucket" json:"Bucket"` Bucket sql.NullInt64 `db:"trns_bucket" json:"Bucket"`
BucketName sql.NullString `db:"bucket_name" json:"BucketName"` BucketName sql.NullString `db:"bucket_name" json:"BucketName"`
Date time.Time `db:"trns_date" json:"TransactionDate"` Date time.Time `db:"trns_date" json:"TransactionDate"`
} }
type QuickTransactionType struct { type QuickTransactionType struct {
DisplayName string DisplayName string
} }
type ChartjsData struct { type ChartjsData struct {
Labels []string `json:"labels"` Labels []string `json:"labels"`
Data []int `json:"data"` Data []int `json:"data"`
} }
type TwoIntsItem struct { type TwoIntsItem struct {
Item1 int Item1 int
Item2 int Item2 int
} }

View file

@ -1,7 +1,7 @@
package web package web
import ( import (
"nickiel.net/recount_server/web/templates" "nickiel.net/recount_server/web/templates"
"context" "context"
"database/sql" "database/sql"
@ -15,11 +15,10 @@ import (
"nickiel.net/recount_server/types" "nickiel.net/recount_server/types"
) )
const DEFAULT_RESULT_COUNT = 20
const DEFAULT_RESULT_COUNT = 20;
func GetTransactionsRows(w http.ResponseWriter, req *http.Request) { func GetTransactionsRows(w http.ResponseWriter, req *http.Request) {
transactions := make([]types.HumanLegibleTransaction, 10) transactions := make([]types.HumanLegibleTransaction, 10)
// Populate the slice with dummy data (you can replace this with your actual data) // Populate the slice with dummy data (you can replace this with your actual data)
for i := 10; i > 0; i-- { for i := 10; i > 0; i-- {
@ -34,15 +33,15 @@ func GetTransactionsRows(w http.ResponseWriter, req *http.Request) {
Date: time.Now(), Date: time.Now(),
} }
transactions[10 - i] = transaction transactions[10-i] = transaction
} }
component := templates.TransactionRows(&transactions); component := templates.TransactionRows(&transactions)
component.Render(context.Background(), w); component.Render(context.Background(), w)
} }
func GetTransactionQuickAccessEntries(w http.ResponseWriter, req *http.Request) { func GetTransactionQuickAccessEntries(w http.ResponseWriter, req *http.Request) {
transactions := make([]types.HumanLegibleTransaction, 20) transactions := make([]types.HumanLegibleTransaction, 20)
// Populate the slice with dummy data (you can replace this with your actual data) // Populate the slice with dummy data (you can replace this with your actual data)
for i := 0; i < 20; i++ { for i := 0; i < 20; i++ {
@ -59,50 +58,50 @@ func GetTransactionQuickAccessEntries(w http.ResponseWriter, req *http.Request)
transactions[i] = transaction transactions[i] = transaction
} }
component := templates.TransactionQuickAccessEntries(&transactions); component := templates.TransactionQuickAccessEntries(&transactions)
component.Render(context.Background(), w); component.Render(context.Background(), w)
} }
func GetExpenditureChart(w http.ResponseWriter, req *http.Request) { func GetExpenditureChart(w http.ResponseWriter, req *http.Request) {
data_package := struct{ data_package := struct {
Labels []string `json:"labels"` Labels []string `json:"labels"`
IncomeDataset []int `json:"income_data"` IncomeDataset []int `json:"income_data"`
ExpensesDataset []int `json:"expenses_data"` ExpensesDataset []int `json:"expenses_data"`
} { }{
Labels: []string{"Income", "Expenses"}, Labels: []string{"Income", "Expenses"},
IncomeDataset: []int {300, 250}, IncomeDataset: []int{300, 250},
ExpensesDataset: []int {200, 100}, ExpensesDataset: []int{200, 100},
}; }
json_data, err := json.Marshal(data_package); json_data, err := json.Marshal(data_package)
if err != nil { if err != nil {
log.Fatal().Err(err).Msg("Could not jsonify data_package"); log.Fatal().Err(err).Msg("Could not jsonify data_package")
} }
w.Write(json_data); w.Write(json_data)
} }
func GetAccountSummaries(w http.ResponseWriter, req *http.Request) { func GetAccountSummaries(w http.ResponseWriter, req *http.Request) {
accounts := make([]types.TwoIntsItem, 20) accounts := make([]types.TwoIntsItem, 20)
for i := 0; i < 20; i++ { for i := 0; i < 20; i++ {
accounts[i] = types.TwoIntsItem {Item1: i*100, Item2: i+5} accounts[i] = types.TwoIntsItem{Item1: i * 100, Item2: i + 5}
} }
component := templates.AccountSummaryRows(&accounts) component := templates.AccountSummaryRows(&accounts)
component.Render(context.Background(), w) component.Render(context.Background(), w)
} }
func GetAccountSummaryChart(w http.ResponseWriter, req *http.Request) { func GetAccountSummaryChart(w http.ResponseWriter, req *http.Request) {
accountID := chi.URLParam(req, "accountID") accountID := chi.URLParam(req, "accountID")
data_package := types.ChartjsData { data_package := types.ChartjsData{
Labels: []string {accountID, "1/10", "1/17", "1/24"}, Labels: []string{accountID, "1/10", "1/17", "1/24"},
Data: []int {100, 0, -50, 25}, Data: []int{100, 0, -50, 25},
} }
json_data, err := json.Marshal(data_package); json_data, err := json.Marshal(data_package)
if err != nil { if err != nil {
log.Fatal().Err(err).Msg("Could not jsonify data_package"); log.Fatal().Err(err).Msg("Could not jsonify data_package")
} }
w.Write(json_data); w.Write(json_data)
} }

View file

@ -18,10 +18,10 @@ import (
) )
type IndexTemplateModel struct { type IndexTemplateModel struct {
InnerHtml template.HTML InnerHtml template.HTML
ActivePage string ActivePage string
NewTransactionPane template.HTML NewTransactionPane template.HTML
QuickAccessPane template.HTML QuickAccessPane template.HTML
} }
const TemplateDir = "./web/templates/" const TemplateDir = "./web/templates/"
@ -33,111 +33,110 @@ func SetLogLevel(level zerolog.Level) {
func WebRouter() http.Handler { func WebRouter() http.Handler {
r := chi.NewRouter() r := chi.NewRouter()
r.Get("/", getDashboard) r.Get("/", getDashboard)
r.Get("/transactions", getTransactionsPage) r.Get("/transactions", getTransactionsPage)
r.Get("/components/account_summaries", GetAccountSummaries) r.Get("/components/account_summaries", GetAccountSummaries)
r.Get("/components/data/transaction_table_rows", GetTransactionsRows) r.Get("/components/data/transaction_table_rows", GetTransactionsRows)
r.Get("/components/data/expenditure_chart", GetExpenditureChart) r.Get("/components/data/expenditure_chart", GetExpenditureChart)
r.Get("/components/data/account_summary/{accountID}", GetAccountSummaryChart) r.Get("/components/data/account_summary/{accountID}", GetAccountSummaryChart)
r.Get("/components/data/transaction_quick_access", GetTransactionQuickAccessEntries) r.Get("/components/data/transaction_quick_access", GetTransactionQuickAccessEntries)
r.Handle("/static/*", http.StripPrefix("/static/", http.FileServer(http.Dir("web/static/")))) r.Handle("/static/*", http.StripPrefix("/static/", http.FileServer(http.Dir("web/static/"))))
return r return r
} }
//for name, values := range req.Header { //for name, values := range req.Header {
// Loop over all values for the name. // Loop over all values for the name.
// for _, value := range values { // for _, value := range values {
// log.Debug().Msg(name + " " + value); // log.Debug().Msg(name + " " + value);
// } // }
// } // }
func renderFullPage(w http.ResponseWriter, c templ.Component, pageName string) { func renderFullPage(w http.ResponseWriter, c templ.Component, pageName string) {
var main_component bytes.Buffer var main_component bytes.Buffer
// Render the provided templ component // Render the provided templ component
err := c.Render(context.Background(), &main_component) err := c.Render(context.Background(), &main_component)
if err != nil { if err != nil {
log.Fatal().Err(err).Msg("Fatal error reading hello template"); log.Fatal().Err(err).Msg("Fatal error reading hello template")
} }
// get the index template // get the index template
index, err := template.ParseFiles(TemplateDir + "index.html") index, err := template.ParseFiles(TemplateDir + "index.html")
if err != nil { if err != nil {
log.Fatal(). log.Fatal().
Err(err). Err(err).
Msg("Fatal error reading index template") Msg("Fatal error reading index template")
} }
var new_tp bytes.Buffer
var new_tp bytes.Buffer; quick_trans_types, err := db.GetTransPaneEntries(0)
if err != nil {
log.Fatal().Err(err).Msg("Could not get transaction pane entries for user")
}
user_acnts, err := db.GetUserAccounts(0)
if err != nil {
log.Fatal().Err(err).Msg("Could not get accounts for user")
}
user_bkts, err := db.GetUserBuckets(0)
if err != nil {
log.Fatal().Err(err).Msg("Could not get buckets for user")
}
quick_trans_types, err := db.GetTransPaneEntries(0); err = templates.NewTransactionPane(&quick_trans_types, &user_acnts, &user_bkts).Render(context.Background(), &new_tp)
if err != nil { if err != nil {
log.Fatal().Err(err).Msg("Could not get transaction pane entries for user"); log.Fatal().Err(err).Msg("Could not render new transaction pane for index")
} }
user_acnts, err := db.GetUserAccounts(0);
if err != nil {
log.Fatal().Err(err).Msg("Could not get accounts for user");
}
user_bkts, err := db.GetUserBuckets(0);
if err != nil {
log.Fatal().Err(err).Msg("Could not get buckets for user");
}
err = templates.NewTransactionPane(&quick_trans_types, &user_acnts, &user_bkts).Render(context.Background(), &new_tp); var quick_ap bytes.Buffer
if err != nil { err = templates.QuickAccessPane().Render(context.Background(), &quick_ap)
log.Fatal().Err(err).Msg("Could not render new transaction pane for index"); if err != nil {
} log.Fatal().Err(err).Msg("Could not render quick access pane for index")
}
var quick_ap bytes.Buffer; // Inject the templ component html into the index template
err = templates.QuickAccessPane().Render(context.Background(), &quick_ap); err = index.Execute(w,
if err != nil { IndexTemplateModel{
log.Fatal().Err(err).Msg("Could not render quick access pane for index"); InnerHtml: template.HTML(main_component.String()),
} ActivePage: pageName,
NewTransactionPane: template.HTML(new_tp.String()),
// Inject the templ component html into the index template QuickAccessPane: template.HTML(quick_ap.String()),
err = index.Execute(w, })
IndexTemplateModel{ if err != nil {
InnerHtml: template.HTML(main_component.String()), log.Fatal().Err(err).Msg("Fatal error reading hello template")
ActivePage: pageName, }
NewTransactionPane: template.HTML(new_tp.String()),
QuickAccessPane: template.HTML(quick_ap.String()),
});
if err != nil {
log.Fatal().Err(err).Msg("Fatal error reading hello template");
}
} }
func getDashboard(w http.ResponseWriter, req *http.Request) { func getDashboard(w http.ResponseWriter, req *http.Request) {
component := templates.Dashboard(); component := templates.Dashboard()
_, ok := req.Header["Hx-Request"] _, ok := req.Header["Hx-Request"]
if ok { if ok {
err := component.Render(context.Background(), w); err := component.Render(context.Background(), w)
if err != nil { if err != nil {
log.Fatal().Err(err).Msg("Couldn't render dashboard templ template"); log.Fatal().Err(err).Msg("Couldn't render dashboard templ template")
} }
} else { } else {
renderFullPage(w, component, "Dashboard"); renderFullPage(w, component, "Dashboard")
} }
} }
func getTransactionsPage(w http.ResponseWriter, req *http.Request) { func getTransactionsPage(w http.ResponseWriter, req *http.Request) {
accounts := make([]string, 10); accounts := make([]string, 10)
for i := 0; i < 10; i++ { for i := 0; i < 10; i++ {
accounts[i] = strconv.Itoa(i) + " Account"; accounts[i] = strconv.Itoa(i) + " Account"
} }
component := templates.TransactionsPage(1, &accounts); component := templates.TransactionsPage(1, &accounts)
_, ok := req.Header["Hx-Request"] _, ok := req.Header["Hx-Request"]
if ok { if ok {
err := component.Render(context.Background(), w); err := component.Render(context.Background(), w)
if err != nil { if err != nil {
log.Fatal().Err(err).Msg("Couldn't render transactions templ template"); log.Fatal().Err(err).Msg("Couldn't render transactions templ template")
} }
} else { } else {
renderFullPage(w, component, "Transactions") renderFullPage(w, component, "Transactions")
} }
} }

View file

@ -3,7 +3,7 @@ import {register_dropdowns} from "./dropdowns.js";
import { import {
open_new_transaction_pane open_new_transaction_pane
, close_new_transaction_pane , close_new_transaction_pane
, add_new_breakdown , register_breakdown_handlers
} from "./new_transactions.js"; } from "./new_transactions.js";
@ -60,12 +60,12 @@ function register_handlers() {
a_el.addEventListener("click", switch_nav); a_el.addEventListener("click", switch_nav);
}); });
register_dropdowns(); register_dropdowns();
register_breakdown_handlers();
document.querySelector("#open-draft").addEventListener("click", open_drafts); document.querySelector("#open-draft").addEventListener("click", open_drafts);
document.querySelector("#close-transaction-pane").addEventListener("click", close_drafts); document.querySelector("#close-transaction-pane").addEventListener("click", close_drafts);
document.querySelector(".input-sizer > input").addEventListener("input", (event) => { document.querySelector(".input-sizer > input").addEventListener("input", (event) => {
event.target.parentNode.dataset.value = event.target.value event.target.parentNode.dataset.value = event.target.value
}); });
document.getElementById("new-breakdown-btn").addEventListener("click", add_new_breakdown);
close_new_transaction_pane(); close_new_transaction_pane();
} }

View file

@ -48,15 +48,68 @@ function close_new_transaction_pane() {
} }
/**
* @param {Event} event
*/
function on_input(event) {
if (event.target.id === "bottom-most-breakdown-input"
|| document.querySelectorAll("#breakdown-tbody input[type='number']:not(#tax-input)").length === 0
) {
add_new_breakdown();
}
let total_input = document.getElementById("amount-entry");
if (event.target.id === "amount-entry") {
let tax_entry = document.getElementById("tax-input");
let tax_rate = document.getElementById("new-transaction-pane").dataset.rcntTaxrate;
if (tax_entry) {
tax_entry.value = (parseFloat(total_input.value) - (parseFloat(total_input.value) / (1 + parseFloat(tax_rate)))).toFixed(2);
}
}
let breakdown_sum = Array.from(document
.querySelectorAll("#breakdown-tbody input[type='number']:not(#bottom-most-breakdown-input)"
)).reduce(function(total, el) {
return total + parseFloat(el.value || 0);
}, 0);
if (total_input.value) {
let value_total = parseFloat(total_input.value);
document.getElementById("bottom-most-breakdown-input").value = (value_total - breakdown_sum).toFixed(2);
} else {
total_input.placeholder = breakdown_sum;
}
}
function add_new_breakdown() { function add_new_breakdown() {
let source = document.querySelector("#breakdown-template-row tr").cloneNode(true); let input_latest_old = document.getElementById("bottom-most-breakdown-input");
source.style.removeProperty("display"); if (input_latest_old) {
console.log(source.querySelector("input[type='number']")); input_latest_old.id = "";
}
let source = document.importNode(document.getElementById("breakdown-template-row").content, true);
let input_latest = source.querySelector("input[type='number']");
input_latest.addEventListener("input", on_input);
input_latest.id = "bottom-most-breakdown-input";
document.getElementById("breakdown-tbody").appendChild(source); document.getElementById("breakdown-tbody").appendChild(source);
} }
function update_breakdown_amount() { function insert_tax() {
if (document.getElementById("tax-input")) return;
let source = document.importNode(document.getElementById("breakdown-template-row").content, true);
source.querySelector("input[type='text']").value = "Tax";
source.querySelector("input[type='number']").id = "tax-input";
let table_body = document.getElementById("breakdown-tbody");
table_body.insertBefore(source, table_body.firstChild);
// on_input();
} }
export {open_new_transaction_pane, close_new_transaction_pane, add_new_breakdown} function register_breakdown_handlers() {
document.getElementById("amount-entry").addEventListener("input", on_input);
document.getElementById("new-breakdown-btn").addEventListener("click", add_new_breakdown);
document.getElementById("tax-breakdown-btn").addEventListener("click", insert_tax);
}
export {open_new_transaction_pane, close_new_transaction_pane, register_breakdown_handlers}

View file

@ -5,7 +5,8 @@ import "strconv"
templ NewTransactionPane(entry_types *[]types.QuickTransactionType, acnts *[]types.Account, buckets *[]types.Bucket) { templ NewTransactionPane(entry_types *[]types.QuickTransactionType, acnts *[]types.Account, buckets *[]types.Bucket) {
<div style="display: none" id="breakdown-template-row"> <template id="breakdown-template-row">
<tr>
<td></td> <td></td>
<td class="my-2"> <td class="my-2">
<select class="ms-2 select border" id="acnt-entry-selection"> <select class="ms-2 select border" id="acnt-entry-selection">
@ -16,8 +17,9 @@ templ NewTransactionPane(entry_types *[]types.QuickTransactionType, acnts *[]typ
</td> </td>
<td><input type="text" /></td> <td><input type="text" /></td>
<td><input type="number" step="any" /></td> <td><input type="number" step="any" /></td>
</div> </tr>
<div id="new-transaction-pane" style="opacity: 0;" class="cr-all c-base d-flex-col"> </template>
<div id="new-transaction-pane" style="opacity: 0;" class="cr-all c-base d-flex-col" data-rcnt-taxrate="0.095">
<div class="my-2 d-flex"> <div class="my-2 d-flex">
<h2 class="ms-5">New Transaction</h2> <h2 class="ms-5">New Transaction</h2>
<button class="ms-auto me-4 my-auto exit-btn" id="close-transaction-pane"> <button class="ms-auto me-4 my-auto exit-btn" id="close-transaction-pane">
@ -84,7 +86,7 @@ templ NewTransactionPane(entry_types *[]types.QuickTransactionType, acnts *[]typ
<i class="" data-feather="plus"></i> <i class="" data-feather="plus"></i>
<span class="my-auto pe-2">New</span> <span class="my-auto pe-2">New</span>
</button> </button>
<button class="btn-sm ms-5 my-auto c-green invert"> <button class="btn-sm ms-5 my-auto c-green invert" id="tax-breakdown-btn">
<i class="" data-feather="plus"></i> <i class="" data-feather="plus"></i>
<span class="my-auto pe-2">Tax</span> <span class="my-auto pe-2">Tax</span>
</button> </button>