added node modules for chartjs :(
This commit is contained in:
parent
c20830a334
commit
656e665ad7
12 changed files with 154 additions and 30 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -6,6 +6,7 @@ web/*_templ.go
|
||||||
web/static/*.css
|
web/static/*.css
|
||||||
web/static/*.css.map
|
web/static/*.css.map
|
||||||
.sass-cache/*
|
.sass-cache/*
|
||||||
|
*/node_modules/*
|
||||||
|
|
||||||
# --> nix
|
# --> nix
|
||||||
.direnv/
|
.direnv/
|
||||||
|
|
|
@ -75,6 +75,7 @@
|
||||||
default = pkgs.mkShell {
|
default = pkgs.mkShell {
|
||||||
buildInputs = with pkgs; [
|
buildInputs = with pkgs; [
|
||||||
air # live go app reloading
|
air # live go app reloading
|
||||||
|
corepack # npm install for the web folder
|
||||||
dart-sass
|
dart-sass
|
||||||
go
|
go
|
||||||
gopls
|
gopls
|
||||||
|
|
|
@ -25,3 +25,8 @@ type HumanLegibleTransaction struct {
|
||||||
BucketName sql.NullString `db:"bucket_name" json:"BucketName"`
|
BucketName sql.NullString `db:"bucket_name" json:"BucketName"`
|
||||||
Date time.Time `db:"trns_date" json:"TransactionDate"`
|
Date time.Time `db:"trns_date" json:"TransactionDate"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type ChartjsData struct {
|
||||||
|
Labels []string `json:"labels"`
|
||||||
|
Data []int `json:"data"`
|
||||||
|
}
|
||||||
|
|
|
@ -9,6 +9,18 @@ templ dashboard() {
|
||||||
<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 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>
|
<i class="my-auto c-text py-3 pe-3 me-1 ms-auto" data-feather="arrow-right"></i>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="d-flex">
|
||||||
|
<div class="my-auto mx-2 w-75">
|
||||||
|
<canvas
|
||||||
|
class="chartjs-chart"
|
||||||
|
data-chart-endpoint="/web/dashboard/expenditure_chart"
|
||||||
|
data-chart-type="bar"
|
||||||
|
id="IncomeVsExpenditureChart"
|
||||||
|
></canvas>
|
||||||
|
</div>
|
||||||
|
<div class="c-mantle">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
<div class="c-crust col card mx-auto cr-all">
|
<div class="c-crust col card mx-auto cr-all">
|
||||||
|
|
53
web/data_endpoints.go
Normal file
53
web/data_endpoints.go
Normal file
|
@ -0,0 +1,53 @@
|
||||||
|
package web
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"database/sql"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/rs/zerolog/log"
|
||||||
|
"nickiel.net/recount_server/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
func getExpenditureChart(w http.ResponseWriter, req *http.Request) {
|
||||||
|
data_package := types.ChartjsData {
|
||||||
|
Labels: []string{"Income", "Expenses"},
|
||||||
|
Data: []int{500, 200},
|
||||||
|
};
|
||||||
|
|
||||||
|
json_data, err := json.Marshal(data_package);
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal().Err(err).Msg("Could not jsonify data_package");
|
||||||
|
}
|
||||||
|
|
||||||
|
w.Write(json_data);
|
||||||
|
}
|
28
web/package-lock.json
generated
Normal file
28
web/package-lock.json
generated
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
{
|
||||||
|
"name": "web",
|
||||||
|
"lockfileVersion": 3,
|
||||||
|
"requires": true,
|
||||||
|
"packages": {
|
||||||
|
"": {
|
||||||
|
"dependencies": {
|
||||||
|
"chart.js": "^4.4.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@kurkle/color": {
|
||||||
|
"version": "0.3.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@kurkle/color/-/color-0.3.2.tgz",
|
||||||
|
"integrity": "sha512-fuscdXJ9G1qb7W8VdHi+IwRqij3lBkosAm4ydQtEmbY58OzHXqQhvlxqEkoz0yssNVn38bcpRWgA9PP+OGoisw=="
|
||||||
|
},
|
||||||
|
"node_modules/chart.js": {
|
||||||
|
"version": "4.4.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/chart.js/-/chart.js-4.4.1.tgz",
|
||||||
|
"integrity": "sha512-C74QN1bxwV1v2PEujhmKjOZ7iUM4w6BWs23Md/6aOZZSlwMzeCIDGuZay++rBgChYru7/+QFeoQW0fQoP534Dg==",
|
||||||
|
"dependencies": {
|
||||||
|
"@kurkle/color": "^0.3.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"pnpm": ">=7"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
5
web/package.json
Normal file
5
web/package.json
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
{
|
||||||
|
"dependencies": {
|
||||||
|
"chart.js": "^4.4.1"
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,20 +1,16 @@
|
||||||
package web
|
package web
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"database/sql"
|
|
||||||
"html/template"
|
"html/template"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
"bytes"
|
"bytes"
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/a-h/templ"
|
"github.com/a-h/templ"
|
||||||
"github.com/go-chi/chi/v5"
|
"github.com/go-chi/chi/v5"
|
||||||
"github.com/rs/zerolog"
|
"github.com/rs/zerolog"
|
||||||
"github.com/rs/zerolog/log"
|
"github.com/rs/zerolog/log"
|
||||||
"nickiel.net/recount_server/types"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type TemplateState struct {
|
type TemplateState struct {
|
||||||
|
@ -32,6 +28,7 @@ func WebRouter() http.Handler {
|
||||||
r := chi.NewRouter()
|
r := chi.NewRouter()
|
||||||
r.Get("/", getIndex)
|
r.Get("/", getIndex)
|
||||||
r.Get("/web/transaction_table_rows", getTransactions)
|
r.Get("/web/transaction_table_rows", getTransactions)
|
||||||
|
r.Get("/web/dashboard/expenditure_chart", getExpenditureChart)
|
||||||
r.Get("/hello", getHello)
|
r.Get("/hello", getHello)
|
||||||
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
|
||||||
|
@ -99,27 +96,3 @@ func getHello(w http.ResponseWriter, req *http.Request) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
|
@ -56,6 +56,9 @@ $directions: (
|
||||||
.w-full {
|
.w-full {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
.w-75 {
|
||||||
|
width: 75%;
|
||||||
|
}
|
||||||
.w-50 {
|
.w-50 {
|
||||||
width: 50%;
|
width: 50%;
|
||||||
}
|
}
|
||||||
|
|
39
web/static/chart_functions.js
Normal file
39
web/static/chart_functions.js
Normal file
|
@ -0,0 +1,39 @@
|
||||||
|
|
||||||
|
function fill_charts() {
|
||||||
|
document.querySelectorAll(".chartjs-chart").forEach(function (el) {
|
||||||
|
var url = el.dataset.chartEndpoint;
|
||||||
|
var type = el.dataset.chartType;
|
||||||
|
|
||||||
|
fetch(url)
|
||||||
|
.then(response => {
|
||||||
|
if (!response.ok) {
|
||||||
|
console.log(response);
|
||||||
|
throw new Error("Fetch response was not ok!")
|
||||||
|
}
|
||||||
|
return response.json();
|
||||||
|
}).then(jsonData => {
|
||||||
|
const config = {
|
||||||
|
type: type,
|
||||||
|
data: {
|
||||||
|
labels: jsonData.labels,
|
||||||
|
datasets: [{
|
||||||
|
data: jsonData.data
|
||||||
|
}]
|
||||||
|
},
|
||||||
|
options: {
|
||||||
|
scales: {
|
||||||
|
y: {
|
||||||
|
beginAtZero: true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
new Chart(el, config)();
|
||||||
|
}).catch(error => {
|
||||||
|
console.error("Unable to set up chart: ", error)
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export {fill_charts}
|
|
@ -1,3 +1,5 @@
|
||||||
|
import {fill_charts} from "./chart_functions.js";
|
||||||
|
|
||||||
|
|
||||||
function debounce(func, delay) {
|
function debounce(func, delay) {
|
||||||
let timeoutId;
|
let timeoutId;
|
||||||
|
@ -48,4 +50,4 @@ function load_in_table() {
|
||||||
|
|
||||||
const trigger_table_animation = debounce(load_in_table, 100);
|
const trigger_table_animation = debounce(load_in_table, 100);
|
||||||
|
|
||||||
export {register_handlers, trigger_table_animation};
|
export {register_handlers, trigger_table_animation, fill_charts};
|
||||||
|
|
|
@ -60,13 +60,15 @@
|
||||||
</div>
|
</div>
|
||||||
<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, trigger_table_animation} from "/static/index.js";
|
import {register_handlers, fill_charts, trigger_table_animation} from "/static/index.js";
|
||||||
register_handlers();
|
register_handlers();
|
||||||
feather.replace();
|
feather.replace();
|
||||||
htmx.onLoad(function (element) {
|
htmx.onLoad(function (element) {
|
||||||
if (element.localName === "tr") {
|
if (element.localName === "tr") {
|
||||||
console.log(element);
|
console.log(element);
|
||||||
trigger_table_animation();
|
trigger_table_animation();
|
||||||
|
} else {
|
||||||
|
fill_charts();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
Loading…
Reference in a new issue