From 0c85aa0880f2c58b523c0b326d7852d9d53ea6f6 Mon Sep 17 00:00:00 2001 From: Tim Wundenberg Date: Fri, 16 May 2025 09:48:22 +0200 Subject: [PATCH] feat(transaction): #66 fix inputs --- handler/transaction.go | 13 +++++++------ service/account.go | 4 ++-- service/default.go | 6 +++--- service/transaction.go | 27 ++++++++++++++++++-------- service/treasure_chest.go | 4 ++-- static/js/time.js | 27 ++++++++++++++++++++++++++ template/layout.templ | 1 + template/transaction/transaction.templ | 16 +++++++-------- types/transaction.go | 13 +++++++------ 9 files changed, 76 insertions(+), 35 deletions(-) create mode 100644 static/js/time.js diff --git a/handler/transaction.go b/handler/transaction.go index f575352..089b2a2 100644 --- a/handler/transaction.go +++ b/handler/transaction.go @@ -127,12 +127,13 @@ func (h TransactionImpl) handleUpdateTransaction() http.HandlerFunc { 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"), - Note: r.FormValue("note"), + 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" { diff --git a/service/account.go b/service/account.go index 8ed5f62..1b1d53f 100644 --- a/service/account.go +++ b/service/account.go @@ -61,7 +61,7 @@ func (s AccountImpl) Add(user *types.User, name string) (*types.Account, error) return nil, types.ErrInternal } - err = validateString(name) + err = validateString(name, "name") if err != nil { return nil, err } @@ -100,7 +100,7 @@ func (s AccountImpl) Update(user *types.User, id string, name string) (*types.Ac if user == nil { return nil, ErrUnauthorized } - err := validateString(name) + err := validateString(name, "name") if err != nil { return nil, err } diff --git a/service/default.go b/service/default.go index 08a765a..ea744e9 100644 --- a/service/default.go +++ b/service/default.go @@ -2,11 +2,11 @@ package service import "fmt" -func validateString(value string) error { +func validateString(value string, fieldName string) error { if value == "" { - return fmt.Errorf("field \"name\" needs to be set: %w", ErrBadRequest) + return fmt.Errorf("field \"%s\" needs to be set: %w", fieldName, ErrBadRequest) } else if !safeInputRegex.MatchString(value) { - return fmt.Errorf("use only letters, dashes and spaces for \"name\": %w", ErrBadRequest) + return fmt.Errorf("use only letters, dashes and spaces for \"%s\": %w", fieldName, ErrBadRequest) } else { return nil } diff --git a/service/transaction.go b/service/transaction.go index e209f03..5e18c85 100644 --- a/service/transaction.go +++ b/service/transaction.go @@ -130,8 +130,8 @@ func (s TransactionImpl) Get(user *types.User, id string) (*types.Transaction, e return nil, fmt.Errorf("could not parse Id: %w", ErrBadRequest) } - var transaction *types.Transaction - err = s.db.Select(transaction, `SELECT * FROM "transaction" WHERE user_id = ? AND id = ?`, user.Id, uuid) + var transaction types.Transaction + err = s.db.Get(&transaction, `SELECT * FROM "transaction" WHERE user_id = ? AND id = ?`, user.Id, uuid) err = db.TransformAndLogDbError("transaction Get", nil, err) if err != nil { if err == db.ErrNotFound { @@ -140,7 +140,7 @@ func (s TransactionImpl) Get(user *types.User, id string) (*types.Transaction, e return nil, types.ErrInternal } - return transaction, nil + return &transaction, nil } func (s TransactionImpl) GetAll(user *types.User) ([]*types.Transaction, error) { @@ -244,21 +244,32 @@ func (s TransactionImpl) validateAndEnrichTransaction(transaction *types.Transac } } - valueInt, err := strconv.ParseInt(input.Value, 10, 64) + valueFloat, err := strconv.ParseFloat(input.Value, 64) if err != nil { log.Error("transaction validate: %v", err) return nil, fmt.Errorf("could not parse value: %w", ErrBadRequest) } + valueInt := int64(valueFloat * 100) - timestampTime, err := time.Parse(time.RFC3339, input.Timestamp) + timestampTime, err := time.Parse("2006-01-02T15:04", input.Timestamp) if err != nil { log.Error("transaction validate: %v", err) return nil, fmt.Errorf("could not parse timestamp: %w", ErrBadRequest) } - err = validateString(input.Note) - if err != nil { - return nil, err + if input.TimezoneOffsetMinutes != "" { + timezoneOffsetMinutes, err := strconv.Atoi(input.TimezoneOffsetMinutes) + if err != nil { + return nil, fmt.Errorf("could not parse timezone offset: %w", ErrBadRequest) + } + timestampTime = timestampTime.Add(time.Duration(-1*timezoneOffsetMinutes) * time.Minute) + } + + if input.Note != "" { + err = validateString(input.Note, "note") + if err != nil { + return nil, err + } } return &types.Transaction{ diff --git a/service/treasure_chest.go b/service/treasure_chest.go index 522b5b3..445d2a4 100644 --- a/service/treasure_chest.go +++ b/service/treasure_chest.go @@ -58,7 +58,7 @@ func (s TreasureChestImpl) Add(user *types.User, parentId, name string) (*types. return nil, types.ErrInternal } - err = validateString(name) + err = validateString(name, "name") if err != nil { return nil, err } @@ -108,7 +108,7 @@ func (s TreasureChestImpl) Update(user *types.User, idStr, parentId, name string if user == nil { return nil, ErrUnauthorized } - err := validateString(name) + err := validateString(name, "name") if err != nil { return nil, err } diff --git a/static/js/time.js b/static/js/time.js new file mode 100644 index 0000000..fe0a8c6 --- /dev/null +++ b/static/js/time.js @@ -0,0 +1,27 @@ + +htmx.on("htmx:afterSwap", (e) => { + updateTime(e.target); +}); + +document.addEventListener("DOMContentLoaded", () => { + console.log("DOMContentLoaded"); + updateTime(document); +}) + +function updateTime(e) { + const timezoneOffset = - new Date().getTimezoneOffset(); + e.querySelectorAll("#timezone-offset").forEach((el) => { + el.value = timezoneOffset; + }); + document.querySelectorAll(".datetime").forEach((el) => { + if (el.textContent !== "") { + el.textContent = el.textContent.includes("UTC") ? new Date(el.textContent).toLocaleString([], { day: 'numeric', month: 'short', year: 'numeric', hour: '2-digit', minute: '2-digit' }) : el.textContent; + } else if (el.attributes['value'] !== "") { + const value = el.attributes['value'].value; + const newDate = value.includes("UTC") ? new Date(value) : value; + newDate.setTime(newDate.getTime() + timezoneOffset * 60 * 1000); + el.valueAsDate = newDate; + } + }) +} + diff --git a/template/layout.templ b/template/layout.templ index 8a6192f..b29c9a7 100644 --- a/template/layout.templ +++ b/template/layout.templ @@ -26,6 +26,7 @@ templ Layout(slot templ.Component, user templ.Component, loggedIn bool, path str /> + // Header diff --git a/template/transaction/transaction.templ b/template/transaction/transaction.templ index cd13b30..14696e9 100644 --- a/template/transaction/transaction.templ +++ b/template/transaction/transaction.templ @@ -18,7 +18,6 @@ templ Transaction(transactions []*types.Transaction, accounts []*types.Account,

New Transaction

- @EditTransaction(nil, accounts, treasureChests) for _, transaction := range transactions { @TransactionItem(transaction) } @@ -39,7 +38,7 @@ templ EditTransaction(transaction *types.Transaction, accounts []*types.Account, cancelUrl string ) if transaction == nil { - timestamp = time.Now() + timestamp = time.Now().UTC().Truncate(time.Minute) note = "" // accountId = "" // treasureChestId = "" @@ -48,7 +47,7 @@ templ EditTransaction(transaction *types.Transaction, accounts []*types.Account, id = "new" cancelUrl = "/empty" } else { - timestamp = transaction.Timestamp + timestamp = transaction.Timestamp.UTC().Truncate(time.Minute) note = transaction.Note // accountId = transaction.AccountId.String() // treasureChestId = transaction.TreasureChestId.String() @@ -66,14 +65,15 @@ templ EditTransaction(transaction *types.Transaction, accounts []*types.Account, class="text-xl flex justify-end gap-4 items-center" >
- + +
-

{ transaction.Timestamp.String() }

+

{ transaction.Timestamp.String() }

{ displayBalance(transaction.Value) }