added transaction page
This commit is contained in:
parent
eb339feb6b
commit
48470e905a
11 changed files with 402 additions and 48 deletions
195
2
Normal file
195
2
Normal file
|
@ -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;
|
||||||
|
}
|
|
@ -16,7 +16,7 @@ import (
|
||||||
|
|
||||||
const DEFAULT_RESULT_COUNT = 20;
|
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)
|
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)
|
||||||
|
|
|
@ -1,7 +0,0 @@
|
||||||
package web
|
|
||||||
|
|
||||||
templ hello(name string) {
|
|
||||||
<title>Hello</title>
|
|
||||||
<div>Hello { name } </div>
|
|
||||||
<h1>SUPRISE!!!</h1>
|
|
||||||
}
|
|
|
@ -26,12 +26,12 @@ func SetLogLevel(level zerolog.Level) {
|
||||||
|
|
||||||
func WebRouter() http.Handler {
|
func WebRouter() http.Handler {
|
||||||
r := chi.NewRouter()
|
r := chi.NewRouter()
|
||||||
r.Get("/", getIndex)
|
r.Get("/", getDashboard)
|
||||||
r.Get("/web/transaction_table_rows", getTransactions)
|
r.Get("/transactions", getTransactionsPage)
|
||||||
|
r.Get("/web/transaction_table_rows", getTransactionsRows)
|
||||||
r.Get("/web/account_summaries", getAccountSummaries)
|
r.Get("/web/account_summaries", getAccountSummaries)
|
||||||
r.Get("/web/dashboard/expenditure_chart", getExpenditureChart)
|
r.Get("/web/dashboard/expenditure_chart", getExpenditureChart)
|
||||||
r.Get("/web/dashboard/account_summary/{accountID}", getAccountSummaryChart)
|
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("/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/"))))
|
r.Handle("/static/*", http.StripPrefix("/static/", http.FileServer(http.Dir("web/static/"))))
|
||||||
return r
|
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();
|
component := dashboard();
|
||||||
|
|
||||||
_, ok := req.Header["Hx-Request"]
|
_, 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");
|
log.Fatal().Err(err).Msg("Couldn't render dashboard templ template");
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
renderFullPage(w, component, "index");
|
renderFullPage(w, component, "Dashboard");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func getHello(w http.ResponseWriter, req *http.Request) {
|
func getTransactionsPage(w http.ResponseWriter, req *http.Request) {
|
||||||
component := hello("Nick")
|
component := transactions_page(1);
|
||||||
|
|
||||||
_, 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 transactions templ template");
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
renderFullPage(w, component, "hello");
|
renderFullPage(w, component, "Transactions")
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
|
@ -16,13 +16,6 @@ body {
|
||||||
height: 97vh;
|
height: 97vh;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
.container {
|
|
||||||
width: 100%;
|
|
||||||
margin-right: auto;
|
|
||||||
margin-left: auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
.row {
|
.row {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
|
|
|
@ -117,6 +117,21 @@ $w_h_sizes: (
|
||||||
text-align: center;
|
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 {
|
table.table {
|
||||||
color: var(--#{$prefix}-text);
|
color: var(--#{$prefix}-text);
|
||||||
td {
|
td {
|
||||||
|
@ -164,3 +179,17 @@ table.table-striped {
|
||||||
color: var(--#{$prefix}-red);
|
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;
|
||||||
|
}
|
||||||
|
|
|
@ -99,14 +99,19 @@ function sparkline_summary_chart(jsonData, element) {
|
||||||
const style = getComputedStyle(document.body);
|
const style = getComputedStyle(document.body);
|
||||||
const red_color = style.getPropertyValue("--pf-red");
|
const red_color = style.getPropertyValue("--pf-red");
|
||||||
const green_color = style.getPropertyValue("--pf-green");
|
const green_color = style.getPropertyValue("--pf-green");
|
||||||
const legend_bg = style.getPropertyValue("--pf-overlay2");
|
const line_color = style.getPropertyValue("--pf-overlay2");
|
||||||
const config = {
|
const config = {
|
||||||
type: "line",
|
type: "line",
|
||||||
data: {
|
data: {
|
||||||
labels: jsonData.labels,
|
labels: jsonData.labels,
|
||||||
datasets: [{
|
datasets: [{
|
||||||
label: "Historical",
|
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: {
|
options: {
|
||||||
|
|
70
web/static/dropdowns.js
Normal file
70
web/static/dropdowns.js
Normal file
|
@ -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}
|
|
@ -1,4 +1,5 @@
|
||||||
import {fill_charts} from "./chart_functions.js";
|
import {fill_charts} from "./chart_functions.js";
|
||||||
|
import {register_dropdowns} from "./dropdowns.js";
|
||||||
|
|
||||||
|
|
||||||
function debounce(func, delay) {
|
function debounce(func, delay) {
|
||||||
|
@ -13,25 +14,29 @@ function debounce(func, delay) {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
function register_nav_links() {
|
function switch_nav(event) {
|
||||||
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");
|
var active_anchor = document.querySelector("nav a.active");
|
||||||
active_anchor.classList.remove("active");
|
active_anchor.classList.remove("active");
|
||||||
|
|
||||||
var clicked = event.target;
|
var clicked = event.target;
|
||||||
clicked.classList.add("active");
|
clicked.classList.add("active");
|
||||||
});
|
}
|
||||||
|
|
||||||
|
function register_nav_links() {
|
||||||
|
var navAnchors = document.querySelectorAll("nav a");
|
||||||
|
navAnchors.forEach(function (a_el) {
|
||||||
|
a_el.removeEventListener("click", switch_nav);
|
||||||
|
a_el.addEventListener("click", switch_nav);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
function register_handlers() {
|
function register_handlers() {
|
||||||
register_nav_links();
|
register_nav_links();
|
||||||
|
fill_charts();
|
||||||
|
register_dropdowns();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
function load_in_table() {
|
function load_in_table() {
|
||||||
var rows = Array.from(document.querySelectorAll(".row_awaiting_processing"));
|
var rows = Array.from(document.querySelectorAll(".row_awaiting_processing"));
|
||||||
rows.sort((a, b) => b.dataset.rcntTransactionRowPos - a.dataset.rcntTransactionRowPos);
|
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 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};
|
||||||
|
|
|
@ -6,6 +6,7 @@
|
||||||
<link rel="stylesheet" href="/static/site.css">
|
<link rel="stylesheet" href="/static/site.css">
|
||||||
<script src="/static/htmx.min.js"></script>
|
<script src="/static/htmx.min.js"></script>
|
||||||
<script src="/static/feather.min.js"></script>
|
<script src="/static/feather.min.js"></script>
|
||||||
|
<title>{{ .ActivePage }}</title>
|
||||||
</head>
|
</head>
|
||||||
<body class="latte">
|
<body class="latte">
|
||||||
<header>
|
<header>
|
||||||
|
@ -26,26 +27,26 @@
|
||||||
hx-push-url="true"
|
hx-push-url="true"
|
||||||
hx-boost="true"
|
hx-boost="true"
|
||||||
hx-target="#main-body-content"
|
hx-target="#main-body-content"
|
||||||
{{ if (eq .ActivePage "index") }}
|
{{ if (eq .ActivePage "Dashboard") }}
|
||||||
class="active"
|
class="active"
|
||||||
{{ end }}
|
{{ end }}
|
||||||
>
|
>
|
||||||
Home!
|
Dashboard
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<a
|
<a
|
||||||
hx-get="/hello"
|
hx-get="/transactions"
|
||||||
hx-swap="innerHtml swap:0.2s settle:0.2s"
|
hx-swap="innerHtml swap:0.2s settle:0.2s"
|
||||||
hx-push-url="true"
|
hx-push-url="true"
|
||||||
hx-boost="true"
|
hx-boost="true"
|
||||||
hx-target="#main-body-content"
|
hx-target="#main-body-content"
|
||||||
{{ if (eq .ActivePage "hello") }}
|
{{ if (eq .ActivePage "Transactions") }}
|
||||||
class="active"
|
class="active"
|
||||||
{{ end }}
|
{{ end }}
|
||||||
>
|
>
|
||||||
Not home!
|
Transactions
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
@ -61,14 +62,13 @@
|
||||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/4.4.1/chart.umd.min.js" integrity="sha512-CQBWl4fJHWbryGE+Pc7UAxWMUMNMWzWxF4SQo9CgkJIN1kx6djDQZjh3Y8SZ1d+6I+1zze6Z7kHXO7q3UyZAWw==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/4.4.1/chart.umd.min.js" integrity="sha512-CQBWl4fJHWbryGE+Pc7UAxWMUMNMWzWxF4SQo9CgkJIN1kx6djDQZjh3Y8SZ1d+6I+1zze6Z7kHXO7q3UyZAWw==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
|
||||||
<script type="module" src="/static/index.js"></script>
|
<script type="module" src="/static/index.js"></script>
|
||||||
<script type="module">
|
<script type="module">
|
||||||
import {register_handlers, fill_all_charts, trigger_table_animation} from "/static/index.js";
|
import {trigger_reset_handlers, trigger_table_animation} from "/static/index.js";
|
||||||
register_handlers();
|
|
||||||
feather.replace();
|
feather.replace();
|
||||||
htmx.onLoad(function (element) {
|
htmx.onLoad(function (element) {
|
||||||
if (element.localName === "tr") {
|
if (element.localName === "tr") {
|
||||||
trigger_table_animation();
|
trigger_table_animation();
|
||||||
} else {
|
} else {
|
||||||
fill_all_charts();
|
trigger_reset_handlers();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
63
web/transactions.templ
Normal file
63
web/transactions.templ
Normal file
|
@ -0,0 +1,63 @@
|
||||||
|
package web
|
||||||
|
|
||||||
|
templ transactions_page(userID int) {
|
||||||
|
<title>Transactions</title>
|
||||||
|
<div class="d-flex-col mx-4 h-100">
|
||||||
|
<div class="c-crust row cr-all p-5" style="height: fit-content">
|
||||||
|
<button
|
||||||
|
class="borderless-btn dropdown-button"
|
||||||
|
aria-describedby="filter-popup"
|
||||||
|
data-dropdown-target="filter-popup"
|
||||||
|
data-dropdown-direction="bottom-start"
|
||||||
|
data-dropdown-motion="expand-down"
|
||||||
|
>
|
||||||
|
<i class="" data-feather="filter"></i>
|
||||||
|
<span class="ms-3 my-auto">Add Filter</span>
|
||||||
|
</button>
|
||||||
|
<div class="popup-menu" id="filter-popup" role="tooltip" style="display: none; max-height: 0px;">
|
||||||
|
<p>Hey there</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<button class="ms-5 borderless-btn">
|
||||||
|
<span class="my-auto mx-3">No Filters</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="row mt-5">
|
||||||
|
<table class="card-table table table-striped cr-all">
|
||||||
|
<colgroup>
|
||||||
|
<col style="width: 2%"></col>
|
||||||
|
<col style="width: 15%"></col>
|
||||||
|
<col style="width: 30%"></col>
|
||||||
|
<col style="width: 15%"></col>
|
||||||
|
<col style="width: 3%"></col>
|
||||||
|
<col style="width: 1%"></col>
|
||||||
|
</colgroup>
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<div class="d-flex">
|
||||||
|
<span class="my-auto me-0">
|
||||||
|
ID
|
||||||
|
</span>
|
||||||
|
<i class="my-auto ms-0 me-auto" data-feather="chevron-up"></i>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
<td>Account</td>
|
||||||
|
<td>Date</td>
|
||||||
|
<td></td>
|
||||||
|
<td class="t-e" style="width:auto">Amount</td>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody
|
||||||
|
hx-trigger="load delay:0.25s"
|
||||||
|
hx-get="/web/transaction_table_rows"
|
||||||
|
hx-params=""
|
||||||
|
>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
}
|
Loading…
Reference in a new issue