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 }}