From 48470e905a7a9e08974c56bd3f62a99a5c29ac70 Mon Sep 17 00:00:00 2001 From: Nickiel12 Date: Sat, 20 Jan 2024 15:05:06 -0800 Subject: [PATCH] added transaction page --- 2 | 195 ++++++++++++++++++++++++++++++++++ web/data_endpoints.go | 2 +- web/hello.templ | 7 -- web/router.go | 25 ++--- web/sass/site.scss | 7 -- web/sass/utility-classes.scss | 29 +++++ web/static/chart_functions.js | 9 +- web/static/dropdowns.js | 70 ++++++++++++ web/static/index.js | 27 +++-- web/templates/index.html | 16 +-- web/transactions.templ | 63 +++++++++++ 11 files changed, 402 insertions(+), 48 deletions(-) create mode 100644 2 delete mode 100644 web/hello.templ create mode 100644 web/static/dropdowns.js create mode 100644 web/transactions.templ diff --git a/2 b/2 new file mode 100644 index 0000000..6a7b7c0 --- /dev/null +++ b/2 @@ -0,0 +1,195 @@ +$sizes: ( + "auto": auto, + "0": 0px, + "1": 2px, + "2": 5px, + "3": 8px, + "4": 10px, + "5": 15px +); +$directions: ( + "s": "left", + "e": "right", + "t": "top", + "b": "bottom" +); + + + +@each $size, $val in $sizes { + .m-#{$size} { + margin: $val; + } + .p-#{$size} { + padding: $val; + } + @each $dir, $dir-val in $directions { + .m#{$dir}-#{$size} { + margin-#{$dir-val}: $val; + } + .p#{$dir}-#{$size} { + padding-#{$dir-val}: $val; + } + + } + .mx-#{$size} { + margin-left: $val; + margin-right: $val; + } + .my-#{$size} { + margin-top: $val; + margin-bottom: $val; + } + .px-#{$size} { + padding-left: $val; + padding-right: $val; + } + .py-#{$size} { + padding-top: $val; + padding-bottom: $val; + } +} +.m-auto { + margin: auto; +} +.my-auto { + margin-top: auto; + margin-bottom: auto; +} +.mx-auto { + margin-left: auto; + margin-right: auto; +} + +$w_h_sizes: ( + '25': 25%, + '50': 50%, + '75': 75%, + '80': 80%, + '100': 100%, + 'auto': auto, +); + +// Loop through the sizes and generate classes +@each $name, $value in $w_h_sizes { + .h-#{$name} { + height: $value; + } + + .w-#{$name} { + width: $value; + } +} + +.d-flex { + display: flex; +} +.d-flex-col { + display: flex; + flex-direction: column; +} +.d-block { + display: block; +} +.cr-all { + border-radius: $border-radius; +} +.cr-top { + border-radius: $border-radius $border-radius 0px 0px; +} +.cr-right { + border-radius: 0px $border-radius $border-radius 0px; +} +.cr-left { + border-radius: $border-radius 0px 0px $border-radius; +} +.cr-bottom { + border-radius: 0px 0px $border-radius $border-radius; +} + +.t-s { + text-align: start; +} +.t-e { + text-align: end; +} +.t-m { + text-align: center; +} + +.borderless-btn { + display: flex; + color: var(--#{$prefix}-text); + font-size: 1em; + border: none; + padding: 10px 15px 10px 15px; + border-radius: 20px; + background-color: var(--#{$prefix}-surface2); + transition: all 0.1s linear; +} +.borderless-btn:hover { + background-color: var(--#{$prefix}-overlay1); + color: var(--#{$prefix}-nav-active-color); +} + +table.table { + color: var(--#{$prefix}-text); + td { + padding-right: 10px; + } +} + +table.table-striped { + border-collapse: collapse; + overflow: hidden; + thead { + background-color: var(--#{$prefix}-surface0); + } + tbody { + tr { + &.row_awaiting_processing { + background-color: var(--#{$prefix}-bg) !important; + max-width: 0px; + div { + opacity: 0; + max-height: 0px; + } + } + div { + display: block; + overflow: hidden; + max-height: 50px; + transition: max-height 0.6s cubic-bezier(0.02, 0.15, 0.84, 0.98), + opacity 0.5s ease-in; + } + max-width: 100%; + transition: all 0.5s ease-in; + &:nth-child(odd) { + background-color: var(--#{$prefix}-crust); + } + &:nth-child(even) { + background-color: var(--#{$prefix}-mantle); + } + } + } + .positive { + color: var(--#{$prefix}-green); + } + .negative { + color: var(--#{$prefix}-red); + } +} + +.popup-menu { + width: max-content; + height: max-content; + position: absolute; + top: 0; + left: 0; + font-size: 1em; + background-color: var(--#{$prefix}-overlay2); + color: var(--#{$prefix}-bg); + border-radius: 0px 0px $border-radius $border-radius; + transition: all 0.2s linear; + overflow: hidden; +} diff --git a/web/data_endpoints.go b/web/data_endpoints.go index 76fedb8..c66f4a3 100644 --- a/web/data_endpoints.go +++ b/web/data_endpoints.go @@ -16,7 +16,7 @@ import ( const DEFAULT_RESULT_COUNT = 20; -func getTransactions(w http.ResponseWriter, req *http.Request) { +func getTransactionsRows(w http.ResponseWriter, req *http.Request) { transactions := make([]types.HumanLegibleTransaction, 10) // Populate the slice with dummy data (you can replace this with your actual data) diff --git a/web/hello.templ b/web/hello.templ deleted file mode 100644 index 7e9b435..0000000 --- a/web/hello.templ +++ /dev/null @@ -1,7 +0,0 @@ -package web - -templ hello(name string) { - Hello -
Hello { name }
-

SUPRISE!!!

-} diff --git a/web/router.go b/web/router.go index 4333612..b685f9a 100644 --- a/web/router.go +++ b/web/router.go @@ -26,12 +26,12 @@ func SetLogLevel(level zerolog.Level) { func WebRouter() http.Handler { r := chi.NewRouter() - r.Get("/", getIndex) - r.Get("/web/transaction_table_rows", getTransactions) + r.Get("/", getDashboard) + r.Get("/transactions", getTransactionsPage) + r.Get("/web/transaction_table_rows", getTransactionsRows) r.Get("/web/account_summaries", getAccountSummaries) r.Get("/web/dashboard/expenditure_chart", getExpenditureChart) r.Get("/web/dashboard/account_summary/{accountID}", getAccountSummaryChart) - r.Get("/hello", getHello) r.Handle("/chart.js/*", http.StripPrefix("/chart.js/", http.FileServer(http.Dir("web/node_modules/chart.js/")))) r.Handle("/static/*", http.StripPrefix("/static/", http.FileServer(http.Dir("web/static/")))) return r @@ -72,7 +72,7 @@ func renderFullPage(w http.ResponseWriter, c templ.Component, pageName string) { } } -func getIndex(w http.ResponseWriter, req *http.Request) { +func getDashboard(w http.ResponseWriter, req *http.Request) { component := dashboard(); _, ok := req.Header["Hx-Request"] @@ -82,20 +82,21 @@ func getIndex(w http.ResponseWriter, req *http.Request) { log.Fatal().Err(err).Msg("Couldn't render dashboard templ template"); } } else { - renderFullPage(w, component, "index"); + renderFullPage(w, component, "Dashboard"); } } -func getHello(w http.ResponseWriter, req *http.Request) { - component := hello("Nick") +func getTransactionsPage(w http.ResponseWriter, req *http.Request) { + component := transactions_page(1); + _, ok := req.Header["Hx-Request"] if ok { - err := component.Render(context.Background(), w); + err := component.Render(context.Background(), w); if err != nil { - log.Fatal().Err(err).Msg("Couldn't render dashboard templ template"); + log.Fatal().Err(err).Msg("Couldn't render transactions templ template"); } - }else { - renderFullPage(w, component, "hello"); + } else { + renderFullPage(w, component, "Transactions") } -} +} diff --git a/web/sass/site.scss b/web/sass/site.scss index 8e5f7ab..5167e3a 100644 --- a/web/sass/site.scss +++ b/web/sass/site.scss @@ -16,13 +16,6 @@ body { height: 97vh; } - -.container { - width: 100%; - margin-right: auto; - margin-left: auto; -} - .row { display: flex; flex-wrap: wrap; diff --git a/web/sass/utility-classes.scss b/web/sass/utility-classes.scss index 89f7978..6a7b7c0 100644 --- a/web/sass/utility-classes.scss +++ b/web/sass/utility-classes.scss @@ -117,6 +117,21 @@ $w_h_sizes: ( text-align: center; } +.borderless-btn { + display: flex; + color: var(--#{$prefix}-text); + font-size: 1em; + border: none; + padding: 10px 15px 10px 15px; + border-radius: 20px; + background-color: var(--#{$prefix}-surface2); + transition: all 0.1s linear; +} +.borderless-btn:hover { + background-color: var(--#{$prefix}-overlay1); + color: var(--#{$prefix}-nav-active-color); +} + table.table { color: var(--#{$prefix}-text); td { @@ -164,3 +179,17 @@ table.table-striped { color: var(--#{$prefix}-red); } } + +.popup-menu { + width: max-content; + height: max-content; + position: absolute; + top: 0; + left: 0; + font-size: 1em; + background-color: var(--#{$prefix}-overlay2); + color: var(--#{$prefix}-bg); + border-radius: 0px 0px $border-radius $border-radius; + transition: all 0.2s linear; + overflow: hidden; +} diff --git a/web/static/chart_functions.js b/web/static/chart_functions.js index e6b9125..bf81e5d 100644 --- a/web/static/chart_functions.js +++ b/web/static/chart_functions.js @@ -99,14 +99,19 @@ function sparkline_summary_chart(jsonData, element) { const style = getComputedStyle(document.body); const red_color = style.getPropertyValue("--pf-red"); const green_color = style.getPropertyValue("--pf-green"); - const legend_bg = style.getPropertyValue("--pf-overlay2"); + const line_color = style.getPropertyValue("--pf-overlay2"); const config = { type: "line", data: { labels: jsonData.labels, datasets: [{ label: "Historical", - data: jsonData.data + data: jsonData.data, + borderColor: line_color, + backgroundColor: function(context) { + var value = context.dataset.data[context.dataIndex]; + return value < 0 ? red_color : green_color; + }, }] }, options: { diff --git a/web/static/dropdowns.js b/web/static/dropdowns.js new file mode 100644 index 0000000..ef64c63 --- /dev/null +++ b/web/static/dropdowns.js @@ -0,0 +1,70 @@ +import { + computePosition, + flip, + shift, +} from 'https://cdn.jsdelivr.net/npm/@floating-ui/dom@1.5.4/+esm'; + +function update_position(button, dropdown_div) { + const placement = button.dataset.dropdownDirection; + computePosition(button, dropdown_div, { + placement: placement, + middleware: [ + flip(), + shift() + ], + }).then(({x, y}) => { + Object.assign(dropdown_div.style, { + left: `${x}px`, + top: `${y}px`, + }); + }) +} + +function setup_dropdown(button) { + const dropdown_div = document.querySelector("#" + button.dataset.dropdownTarget); + update_position(button, dropdown_div); + return function () { + if (dropdown_div.style.display == "none") { + + dropdown_div.style.removeProperty("display"); + if (button.dataset.dropdownMotion == "expand-down") { + dropdown_div.style.display = "block"; + dropdown_div.style.maxHeight = "0px"; + + button.style.borderBottomLeftRadius = "0px"; + button.style.borderBottomRightRadius = "0px"; + } + + update_position(button, dropdown_div); + + setTimeout(function () { + if (button.dataset.dropdownMotion == "expand-down") { + dropdown_div.style.maxHeight = "100px"; + } else { + dropdown_div.style.opacity = 1; + } + }, 110); + } else { + if (button.dataset.dropdownMotion == "expand-down") { + dropdown_div.style.maxHeight = "0px"; + } else { + dropdown_div.style.opacity = 0; + } + setTimeout(function () { + if (button.dataset.dropdownMotion == "expand-down") { + button.style.removeProperty("border-bottom-left-radius"); + button.style.removeProperty("border-bottom-right-radius"); + } + dropdown_div.style.display = "none"; + }, 200); + } + }; +} + +function register_dropdowns() { + document.querySelectorAll(".dropdown-button").forEach(function (el) { + el.addEventListener("click", setup_dropdown(el)); + }); +} + +export {register_dropdowns} diff --git a/web/static/index.js b/web/static/index.js index 2178eb1..b9bfed8 100644 --- a/web/static/index.js +++ b/web/static/index.js @@ -1,4 +1,5 @@ import {fill_charts} from "./chart_functions.js"; +import {register_dropdowns} from "./dropdowns.js"; function debounce(func, delay) { @@ -13,25 +14,29 @@ function debounce(func, delay) { }; } +function switch_nav(event) { + var active_anchor = document.querySelector("nav a.active"); + active_anchor.classList.remove("active"); + + var clicked = event.target; + clicked.classList.add("active"); +} + function register_nav_links() { var navAnchors = document.querySelectorAll("nav a"); - navAnchors.forEach(function (a_el) { - a_el.addEventListener("click", function(event) { - var active_anchor = document.querySelector("nav a.active"); - active_anchor.classList.remove("active"); - - var clicked = event.target; - clicked.classList.add("active"); - }); + a_el.removeEventListener("click", switch_nav); + a_el.addEventListener("click", switch_nav); }); } + function register_handlers() { register_nav_links(); + fill_charts(); + register_dropdowns(); } - function load_in_table() { var rows = Array.from(document.querySelectorAll(".row_awaiting_processing")); rows.sort((a, b) => b.dataset.rcntTransactionRowPos - a.dataset.rcntTransactionRowPos); @@ -50,6 +55,6 @@ function load_in_table() { const trigger_table_animation = debounce(load_in_table, 100); -const fill_all_charts = debounce(fill_charts, 500); +const trigger_reset_handlers = debounce(register_handlers, 200); -export {register_handlers, trigger_table_animation, fill_all_charts}; +export {trigger_reset_handlers, trigger_table_animation}; diff --git a/web/templates/index.html b/web/templates/index.html index b1ed539..5cc5684 100644 --- a/web/templates/index.html +++ b/web/templates/index.html @@ -6,6 +6,7 @@ + {{ .ActivePage }}
@@ -26,26 +27,26 @@ hx-push-url="true" hx-boost="true" hx-target="#main-body-content" - {{ if (eq .ActivePage "index") }} + {{ if (eq .ActivePage "Dashboard") }} class="active" {{ end }} > - Home! + Dashboard
  • - Not home! + Transactions
  • @@ -61,14 +62,13 @@ diff --git a/web/transactions.templ b/web/transactions.templ new file mode 100644 index 0000000..308fe51 --- /dev/null +++ b/web/transactions.templ @@ -0,0 +1,63 @@ +package web + +templ transactions_page(userID int) { + Transactions +
    +
    + + + + +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    + +
    + + ID + + +
    +
    AccountDateAmount
    +
    +
    +}