added fading in tables
This commit is contained in:
parent
23b335a11d
commit
c20830a334
11 changed files with 250 additions and 35 deletions
|
@ -1,10 +1,9 @@
|
|||
package debug_mode
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"nickiel.net/recount_server/types"
|
||||
"encoding/json"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"github.com/jmoiron/sqlx"
|
||||
_ "github.com/mattn/go-sqlite3"
|
||||
|
@ -12,14 +11,6 @@ import (
|
|||
"github.com/rs/zerolog/log"
|
||||
)
|
||||
|
||||
type Transaction struct {
|
||||
Id int `db:"trns_id" json:"Id"`
|
||||
Amount string `db:"trns_amount" json:"Amount"`
|
||||
Description sql.NullString `db:"trns_description" json:"Description"`
|
||||
Account int `db:"trns_account" json:"Account"`
|
||||
Bucket sql.NullInt64 `db:"trns_bucket" json:"Bucket"`
|
||||
Date time.Time `db:"trns_date" json:"TransactionDate"`
|
||||
}
|
||||
|
||||
func SetLogLevel(level zerolog.Level) {
|
||||
zerolog.SetGlobalLevel(level)
|
||||
|
@ -142,7 +133,7 @@ INSERT INTO transactions (trns_amount, trns_description, trns_account, trns_buck
|
|||
"TransactionDate": "2023-11-11T00:00:00Z"
|
||||
}`
|
||||
|
||||
var trns Transaction = Transaction{}
|
||||
var trns types.Transaction = types.Transaction{}
|
||||
err = json.Unmarshal([]byte(jsonExample), &trns)
|
||||
|
||||
if err != nil {
|
||||
|
|
27
types/types.go
Normal file
27
types/types.go
Normal file
|
@ -0,0 +1,27 @@
|
|||
package types
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
|
||||
"time"
|
||||
)
|
||||
|
||||
type Transaction struct {
|
||||
Id int `db:"trns_id" json:"Id"`
|
||||
Amount string `db:"trns_amount" json:"Amount"`
|
||||
Description sql.NullString `db:"trns_description" json:"Description"`
|
||||
Account int `db:"trns_account" json:"Account"`
|
||||
Bucket sql.NullInt64 `db:"trns_bucket" json:"Bucket"`
|
||||
Date time.Time `db:"trns_date" json:"TransactionDate"`
|
||||
}
|
||||
|
||||
type HumanLegibleTransaction struct {
|
||||
Id int `db:"trns_id" json:"Id"`
|
||||
Amount string `db:"trns_amount" json:"Amount"`
|
||||
Description sql.NullString `db:"trns_description" json:"Description"`
|
||||
AccountName sql.NullString `db:"account_name" json:"AccountName"`
|
||||
Account int `db:"trns_account" json:"Account"`
|
||||
Bucket sql.NullInt64 `db:"trns_bucket" json:"Bucket"`
|
||||
BucketName sql.NullString `db:"bucket_name" json:"BucketName"`
|
||||
Date time.Time `db:"trns_date" json:"TransactionDate"`
|
||||
}
|
|
@ -3,24 +3,55 @@ package web
|
|||
templ dashboard() {
|
||||
<title>Dashboard</title>
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="row" style="margin-top: -20px">
|
||||
<div class="c-crust col card mx-auto cr-all">
|
||||
<div class="c-s0 d-flex w-full cr-top">
|
||||
<i class="my-auto py-3 ps-3 ms-1" data-feather="arrow-left"></i>
|
||||
<i class="my-auto py-3 pe-3 me-1 ms-auto" data-feather="arrow-right"></i>
|
||||
<i class="my-auto c-text py-3 ps-3 ms-1" data-feather="arrow-left"></i>
|
||||
<i class="my-auto c-text py-3 pe-3 me-1 ms-auto" data-feather="arrow-right"></i>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<div class="c-crust col card mx-auto cr-all">
|
||||
<div class="c-s0 d-flex w-full cr-top">
|
||||
<i class="my-auto py-3 ps-3 ms-1" data-feather="arrow-left"></i>
|
||||
<i class="my-auto py-3 pe-3 me-1 ms-auto" data-feather="arrow-right"></i>
|
||||
<i class="my-auto c-text py-3 ps-3 ms-1" data-feather="arrow-left"></i>
|
||||
<i class="my-auto c-text py-3 pe-3 me-1 ms-auto" data-feather="arrow-right"></i>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<table>
|
||||
|
||||
<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>
|
||||
|
|
28
web/dataendpoints.templ
Normal file
28
web/dataendpoints.templ
Normal file
|
@ -0,0 +1,28 @@
|
|||
package web
|
||||
|
||||
import (
|
||||
"nickiel.net/recount_server/types"
|
||||
|
||||
"strconv"
|
||||
)
|
||||
|
||||
templ transaction_rows(transactions *[]types.HumanLegibleTransaction) {
|
||||
for i, value := range *transactions {
|
||||
<tr class="row_awaiting_processing" data-rcnt-transaction-row-pos={strconv.Itoa(i)}>
|
||||
<td></td>
|
||||
<td><div>{strconv.Itoa(value.Id)}</div></td>
|
||||
if value.AccountName.Valid {
|
||||
<td><div>{value.AccountName.String}</div></td>
|
||||
} else {
|
||||
<td><div>{strconv.Itoa(value.Account)}</div></td>
|
||||
}
|
||||
<td><div>{value.Date.Format("01/02/2006")}</div></td>
|
||||
<td><div>$</div></td>
|
||||
if (len(value.Amount) > 0 && value.Amount[0] == '-') {
|
||||
<td class="t-e negative"><div>{value.Amount}</div></td>
|
||||
} else {
|
||||
<td class="t-e positive"><div>+{value.Amount}</div></td>
|
||||
}
|
||||
</tr>
|
||||
}
|
||||
}
|
|
@ -1,16 +1,20 @@
|
|||
package web
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"html/template"
|
||||
"net/http"
|
||||
|
||||
"bytes"
|
||||
"context"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/a-h/templ"
|
||||
"github.com/go-chi/chi/v5"
|
||||
"github.com/rs/zerolog"
|
||||
"github.com/rs/zerolog/log"
|
||||
"nickiel.net/recount_server/types"
|
||||
)
|
||||
|
||||
type TemplateState struct {
|
||||
|
@ -27,6 +31,7 @@ func SetLogLevel(level zerolog.Level) {
|
|||
func WebRouter() http.Handler {
|
||||
r := chi.NewRouter()
|
||||
r.Get("/", getIndex)
|
||||
r.Get("/web/transaction_table_rows", getTransactions)
|
||||
r.Get("/hello", getHello)
|
||||
r.Handle("/static/*", http.StripPrefix("/static/", http.FileServer(http.Dir("web/static/"))))
|
||||
return r
|
||||
|
@ -93,3 +98,28 @@ func getHello(w http.ResponseWriter, req *http.Request) {
|
|||
renderFullPage(w, component, "hello");
|
||||
}
|
||||
}
|
||||
|
||||
const DEFAULT_RESULT_COUNT = 20;
|
||||
|
||||
func getTransactions(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)
|
||||
for i := 10; i > 0; i-- {
|
||||
transaction := types.HumanLegibleTransaction{
|
||||
Id: i,
|
||||
Amount: fmt.Sprintf("%d.00", (i+1)*100),
|
||||
Description: sql.NullString{String: fmt.Sprintf("Transaction %d", i+1), Valid: true},
|
||||
AccountName: sql.NullString{String: "Savings", Valid: true},
|
||||
Account: 123,
|
||||
Bucket: sql.NullInt64{Int64: int64(i + 100), Valid: true},
|
||||
BucketName: sql.NullString{String: fmt.Sprintf("Bucket %d", i+1), Valid: true},
|
||||
Date: time.Now(),
|
||||
}
|
||||
|
||||
transactions[10 - i] = transaction
|
||||
}
|
||||
|
||||
component := transaction_rows(&transactions);
|
||||
component.Render(context.Background(), w);
|
||||
}
|
||||
|
|
|
@ -4,6 +4,7 @@ header {
|
|||
width: 100%;
|
||||
margin-top: 10px;
|
||||
margin-bottom: 10px;
|
||||
border-radius: $border-radius;
|
||||
}
|
||||
nav {
|
||||
display: flex;
|
||||
|
|
|
@ -13,6 +13,7 @@ $border-radius: 8px;
|
|||
|
||||
body {
|
||||
background-color: var(--#{$prefix}-bg);
|
||||
height: 97vh;
|
||||
}
|
||||
|
||||
|
||||
|
@ -38,26 +39,35 @@ body {
|
|||
@media (min-width: 1362px) {
|
||||
.card-table {
|
||||
box-sizing: border-box;
|
||||
width: clamp(300px, 80%, 450px);
|
||||
width: 100%;
|
||||
margin-left: 0%;
|
||||
margin-right: 0%;
|
||||
}
|
||||
}
|
||||
@media (max-width: 1362px) {
|
||||
.card-table {
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
box-sizing: border-box;
|
||||
margin-left: 10%;
|
||||
margin-right: 10%;
|
||||
width: clamp(300px, 80%, 450px);
|
||||
}
|
||||
}
|
||||
|
||||
div#below-header {
|
||||
height: 93vh;
|
||||
}
|
||||
|
||||
div#left-col {
|
||||
width: 15vw;
|
||||
padding: 10px;
|
||||
background-color: var(--#{$prefix}-nav-bg);
|
||||
border-radius: $border-radius;
|
||||
}
|
||||
|
||||
div#main-body-content {
|
||||
width: 70vw;
|
||||
padding: 10px;
|
||||
height: 100%;
|
||||
overflow-y: scroll;
|
||||
opacity: 1;
|
||||
transition: opacity 0.2s ease-out;
|
||||
}
|
||||
|
@ -73,4 +83,5 @@ div#right-col {
|
|||
width: 15vw;
|
||||
padding: 10px;
|
||||
background-color: var(--#{$prefix}-nav-bg);
|
||||
border-radius: $border-radius;
|
||||
}
|
||||
|
|
|
@ -77,3 +77,58 @@ $directions: (
|
|||
.cr-bottom {
|
||||
border-radius: 0px 0px $border-radius $border-radius;
|
||||
}
|
||||
|
||||
.t-s {
|
||||
text-align: start;
|
||||
}
|
||||
.t-e {
|
||||
text-align: end;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,7 +7,13 @@
|
|||
--#{$prefix}-mantle: #{map-get($color, 'mantle')};
|
||||
--#{$prefix}-surface0: #{map-get($color, 'surface0')};
|
||||
--#{$prefix}-surface1: #{map-get($color, 'surface1')};
|
||||
--#{$prefix}-surface2: #{map-get($color, 'surface2')};
|
||||
--#{$prefix}-overlay0: #{map-get($color, 'overlay0')};
|
||||
--#{$prefix}-overlay1: #{map-get($color, 'overlay1')};
|
||||
--#{$prefix}-overlay2: #{map-get($color, 'overlay2')};
|
||||
--#{$prefix}-text: #{map-get($color, 'text')};
|
||||
--#{$prefix}-green: #{map-get($color, 'green')};
|
||||
--#{$prefix}-red: #{map-get($color, 'red')};
|
||||
|
||||
--#{$prefix}-nav-bg: #{map-get($color, 'crust')};
|
||||
--#{$prefix}-nav-color: #{map-get($color, 'text')};
|
||||
|
|
|
@ -1,22 +1,51 @@
|
|||
|
||||
function say_hello() {
|
||||
console.log("Hello World");
|
||||
function debounce(func, delay) {
|
||||
let timeoutId;
|
||||
|
||||
return function (...args) {
|
||||
clearTimeout(timeoutId);
|
||||
|
||||
timeoutId = setTimeout(() => {
|
||||
func.apply(this, args);
|
||||
}, delay);
|
||||
};
|
||||
}
|
||||
|
||||
function register_nav_links() {
|
||||
var navAnchors = document.querySelectorAll("nav a");
|
||||
|
||||
navAnchors.forEach(function (a_el) {
|
||||
a_el.addEventListener("click", nav_a_click);
|
||||
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");
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function nav_a_click(event) {
|
||||
var active_anchor = document.querySelector("nav a.active");
|
||||
active_anchor.classList.remove("active");
|
||||
|
||||
var clicked = event.target;
|
||||
clicked.classList.add("active");
|
||||
function register_handlers() {
|
||||
register_nav_links();
|
||||
}
|
||||
|
||||
export {say_hello, register_nav_links};
|
||||
|
||||
function load_in_table() {
|
||||
var rows = Array.from(document.querySelectorAll(".row_awaiting_processing"));
|
||||
rows.sort((a, b) => b.dataset.rcntTransactionRowPos - a.dataset.rcntTransactionRowPos);
|
||||
|
||||
const processElement = (element, index) => {
|
||||
element.classList.remove('row_awaiting_processing');
|
||||
setTimeout(() => {
|
||||
if (index < rows.length - 1) {
|
||||
processElement(rows[index + 1], index + 1);
|
||||
}
|
||||
}, 25); // 1000 milliseconds (1 second) delay, adjust as needed
|
||||
};
|
||||
|
||||
processElement(rows[0], 0);
|
||||
}
|
||||
|
||||
const trigger_table_animation = debounce(load_in_table, 100);
|
||||
|
||||
export {register_handlers, trigger_table_animation};
|
||||
|
|
|
@ -60,9 +60,15 @@
|
|||
</div>
|
||||
<script type="module" src="/static/index.js"></script>
|
||||
<script type="module">
|
||||
import {register_nav_links} from "/static/index.js";
|
||||
register_nav_links();
|
||||
import {register_handlers, trigger_table_animation} from "/static/index.js";
|
||||
register_handlers();
|
||||
feather.replace();
|
||||
htmx.onLoad(function (element) {
|
||||
if (element.localName === "tr") {
|
||||
console.log(element);
|
||||
trigger_table_animation();
|
||||
}
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
Loading…
Reference in a new issue