change to col-auto everywhere, with some more forcing of width maximums to make the page more consistent, redid defaults to incorporate updated per fortnight lease cost, and updated inflation figure/notes/references. Updated default Living expenses to 84000 to match latest view of data, fixed up compare_to to now work, shows a graph of data to compare with, allows drop-down to be compare_to nothing or a saved set, only 1 hand saved for now. Annotations on graph for large changes in savings now work and are legible - had to allow overlap and do some overly complex left/right up/down offsetting to make them all sensible
This commit is contained in:
@@ -8,38 +8,34 @@
|
||||
<script src="https://code.jquery.com/jquery-3.7.1.min.js" integrity="sha256-/JqT3SQfawRcv/BIHPThkBvs0OEvtFFmqPF/lYI/Cxo=" crossorigin="anonymous"></script>
|
||||
<script src="https://code.highcharts.com/highcharts.js"></script>
|
||||
<script src="https://code.highcharts.com/modules/annotations.js"></script>
|
||||
<script src="https://code.highcharts.com/modules/accessibility.js"></script>
|
||||
<style>
|
||||
.col-form-label {
|
||||
width:170px;
|
||||
}
|
||||
.col-form-label { width:140px; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container-fluid">
|
||||
<div class="containerfluid">
|
||||
<h3 align="center">Finance Tracker</h3>
|
||||
|
||||
<form id="vals_form" class="mt-3" action="/update" method="POST">
|
||||
<form id="vals_form" class="ms-3 mt-3" action="/update" method="POST">
|
||||
{% for r in DISP %}
|
||||
<div class="row align-items-center">
|
||||
{% for el in r %}
|
||||
<div class="{{el.cl}}">
|
||||
<div class="input-group">
|
||||
{% if el.display=="select" %}
|
||||
<label for="{{el.varname}}" class="input-group-text">{{el.label}}</label>
|
||||
<select class="form-select border border-primary text-primary" id="{{el.varname}}" name="{{el.varname}}" onchange="this.form.submit()">
|
||||
<label for="{{el.varname}}" class="col-form-label me-2 text-end float-end">{{el.label}}</label>
|
||||
<select class="form-select border border-primary text-primary" id="{{el.varname}}" name="{{el.varname}}" style="width: 120px;" onchange="this.form.submit()">
|
||||
<option value="0">Never</option>
|
||||
<option value="1">1 years</option>
|
||||
<option value="2">2 years</option>
|
||||
<option value="3">3 years</option>
|
||||
<option value="4">4 years</option>
|
||||
<option value="5">5 years</option>
|
||||
<option value="6">6 years</option>
|
||||
{% for el in range( 1,7 ) %}
|
||||
<option value="{{el}}">{{el}} years</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
{% elif el.display=="date" %}
|
||||
<label for="{{el.varname}}" class="col-form-label me-2 text-end float-end">{{el.label}}</label>
|
||||
<input type="number" step="any" class="form-control text-end float-end border border-primary" onchange="this.form.submit()"
|
||||
<input type="number" step="any" class="form-control text-end float-end border border-primary" onchange="this.form.submit()" style="max-width: 120px;"
|
||||
id="{{el.varname}}" name="{{el.varname}}" value="{{ finance[el.varname] }}" {{el.display}}>
|
||||
<input type="date" class="form-control text-end float-end border border-primary" id="{{el.datevarname}}"
|
||||
<input type="date" class="form-control text-end float-end border border-primary" id="{{el.datevarname}}" style="max-width: 150px;"
|
||||
name="{{el.datevarname}}" value="{{ finance[el.datevarname] }}" onchange="this.form.submit()">
|
||||
{% else %}
|
||||
<label for="{{el.varname}}" class="col-form-label me-2 text-end float-end">{{el.label}}</label>
|
||||
@@ -50,7 +46,7 @@
|
||||
{% set bg="" %}
|
||||
{% set bd="border-1 border-primary" %}
|
||||
{% endif %}
|
||||
<input type="number" step="any" class="{{bg}} form-control text-end float-end {{bd}}" onchange="this.form.submit()"
|
||||
<input type="number" step="any" class="{{bg}} form-control text-end float-end {{bd}}" onchange="this.form.submit()" style="max-width: 120px;"
|
||||
id="{{el.varname}}" name="{{el.varname}}" value="{{ finance[el.varname] }}" {{el.display}}>
|
||||
{% endif %}
|
||||
</div>
|
||||
@@ -58,24 +54,23 @@
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% endfor %}
|
||||
</form>
|
||||
|
||||
|
||||
<h5 align="center" class="mt-4">Fortnighthly Savings data:
|
||||
{% if COMP %}
|
||||
<font color="blue"> Note: value in blue below is value we should have been at when comparing to saved values</font>
|
||||
{# get comparison date so we can use it below in loop to know when to print it out #}
|
||||
{% set comp_yr=COMP['date'][:4] %}
|
||||
{% set comp_mon=COMP['date'][5:7] %}
|
||||
{% set comp_day=COMP['date'][8:10 ] %}
|
||||
{% set comp_done=namespace( val=0 ) %}
|
||||
{% else %}
|
||||
{# we dont need to do a comparison, so consider it done before we begin #}
|
||||
{% set comp_done=namespace( val=1 ) %}
|
||||
{% endif %}
|
||||
</h5>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-auto"> <div class="pt-1 pb-1 mb-0 alert text-center" style="background:lemonchiffon">2025</div>
|
||||
|
||||
{# get comparison date so we can use it below in loop to know when to print it out #}
|
||||
{% set comp_yr=COMP['date'][:4] %}
|
||||
{% set comp_mon=COMP['date'][5:7] %}
|
||||
{% set comp_day=COMP['date'][8:10 ] %}
|
||||
{% set comp_done=namespace( val=0 ) %}
|
||||
|
||||
{% set first_yr=2025 %}
|
||||
{% for date, dollars in savings %}
|
||||
{% set yr=date[:4] %}
|
||||
@@ -103,6 +98,9 @@
|
||||
{% else %}
|
||||
<div class="alert alert-success">Super kicks in!!!</div>
|
||||
{% endif %}
|
||||
{% if COMP %}
|
||||
<div class="alert alert-info">Note: value in blue<br>above is value we<br>should have been<br>at when comparing<br> to saved values</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
<div class="col-auto">
|
||||
<div class="alert alert-warning">
|
||||
@@ -115,18 +113,7 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-auto">
|
||||
<button type="submit" class="btn btn-primary" onClick="$('#vals_form').submit()">Update</button>
|
||||
</div>
|
||||
<div class="col-auto">
|
||||
<div class="input-group">
|
||||
<button type="submit" class="disabled btn btn-primary" onClick="$('#vals_form').submit() disabled">Compare with:</button>
|
||||
<select class="form-select border border-primary text-primary" id="comp_set" name="comp_set" onchange="">
|
||||
<option value="0">None</option>
|
||||
<option value="1">something</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-auto">
|
||||
<button type="button" class="btn btn-primary" onClick="alert('not yet'); return false">Save</button>
|
||||
<button type="button" class="btn btn-primary" onClick="
|
||||
$.ajax( { type: 'GET', url: '/download_csv', xhrFields: { responseType: 'blob' },
|
||||
success: function(res){
|
||||
@@ -142,24 +129,42 @@
|
||||
console.log('done') } })
|
||||
"> Export to CSV </button>
|
||||
</div>
|
||||
<div class="col-auto">
|
||||
<div class="input-group">
|
||||
<button type="submit" class="disabled btn btn-primary" onClick="$('#vals_form').submit() disabled">Compare to:</button>
|
||||
<select class="form-select border border-primary text-primary" id="compare_to" name="compare_to" onchange="$('#vals_form').submit()">
|
||||
{% for el in finance['COMP_SETS'] %}
|
||||
<option value="{{el[0]}}">{{el[1]}}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
<div class="row mt-4" id="container" style="width:100%; height:400px;"></div>
|
||||
<script type="text/javascript">
|
||||
window.onload = function() {
|
||||
document.getElementById("Sell_shares").value = {{finance['Sell_shares']}};
|
||||
};
|
||||
window.onload = function() {
|
||||
$('#Sell_shares').val( {{finance['Sell_shares']}} )
|
||||
$('#compare_to').val( {{finance['compare_to']}} )
|
||||
};
|
||||
|
||||
document.addEventListener('DOMContentLoaded', function () {
|
||||
// Parse the savings_data from Flask
|
||||
const savingsData = JSON.parse('{{ COMP['savings_data'] | tojson }}');
|
||||
const savingsData = JSON.parse('{{ savings | tojson }}');
|
||||
const chartData = savingsData.map(entry => [new Date(entry[0]).getTime(), parseFloat(entry[1])]);
|
||||
{% if COMP %}
|
||||
const compSavingsData = JSON.parse('{{ COMP['savings_data'] | tojson }}');
|
||||
const compChartData = compSavingsData.map(entry => [new Date(entry[0]).getTime(), parseFloat(entry[1])]);
|
||||
{% endif %}
|
||||
|
||||
|
||||
// Get the legend name and vars for the tooltip
|
||||
const legendName = '{{ COMP["vars"]["name"] }}';
|
||||
const vars = JSON.parse('{{ COMP["vars"] | tojson }}');
|
||||
const legendName = 'Savings';
|
||||
const vars = JSON.parse('{{ finance | tojson }}');
|
||||
|
||||
// Tooltip content from vars
|
||||
const tooltipContent = Object.entries(vars).map(([key, value]) => `${key}: ${value}`).join('<br>');
|
||||
console.log(tooltipContent)
|
||||
|
||||
// Calculate plot bands for each year with alternating background colors
|
||||
const plotBands = [];
|
||||
@@ -188,63 +193,58 @@
|
||||
color: year % 2 === 0 ? 'white' : 'lemonchiffon'
|
||||
});
|
||||
|
||||
// Add annotations for changes greater than 5000
|
||||
const annotations = [];
|
||||
{% for a in finance['annotations'] %}
|
||||
console.log( "{{a['x']}}" )
|
||||
console.log( "{{a['y']}}" )
|
||||
console.log( "{{a['label']}}" )
|
||||
annotations.push({
|
||||
labels: [{
|
||||
point: {
|
||||
x: {{a['x']}},
|
||||
y: {{a['y']}},
|
||||
xAxis: 0,
|
||||
yAxis: 0
|
||||
},
|
||||
text: '{{a['label']}}'
|
||||
}]
|
||||
});
|
||||
{% endfor %}
|
||||
var offset=13
|
||||
var al='left'
|
||||
var x=-130
|
||||
var done=0
|
||||
{% if not COMP %}
|
||||
// Add annotations for changes greater than 5000
|
||||
{% for a in finance['annotations'] %}
|
||||
annotations.push({
|
||||
labels: [{
|
||||
point: {
|
||||
x: {{a['x']}},
|
||||
y: {{a['y']}},
|
||||
crop: true,
|
||||
xAxis: 0,
|
||||
yAxis: 0
|
||||
},
|
||||
x: x,
|
||||
y: offset,
|
||||
align: al,
|
||||
text: '{{a['label']}}'
|
||||
}], labelOptions: { allowOverlap: true }
|
||||
});
|
||||
if( offset == 150 ) { offset=100 } else { offset=150 }
|
||||
if( done == 2 ) {
|
||||
if( al=='right' ) { console.log('change to left'); al='left'; x=-130 } else { console.log('change to right'); al='right'; x=130 }
|
||||
done=0
|
||||
}
|
||||
done++
|
||||
{% endfor %}
|
||||
document.keep = annotations
|
||||
{% endif %}
|
||||
|
||||
// Highcharts configuration
|
||||
Highcharts.chart('container', {
|
||||
chart: {
|
||||
type: 'line'
|
||||
},
|
||||
title: {
|
||||
text: 'Savings Over Time'
|
||||
},
|
||||
chart: { type: 'line' },
|
||||
title: { text: 'Savings Over Time' },
|
||||
xAxis: {
|
||||
type: 'datetime',
|
||||
title: {
|
||||
text: 'Date'
|
||||
},
|
||||
title: { text: 'Date' },
|
||||
plotBands: plotBands // Alternating background for years
|
||||
},
|
||||
yAxis: {
|
||||
title: {
|
||||
text: 'Amount ($)'
|
||||
}
|
||||
},
|
||||
legend: {
|
||||
labelFormatter: function () {
|
||||
return `<span title="${tooltipContent}">${legendName}</span>`;
|
||||
}
|
||||
},
|
||||
tooltip: {
|
||||
pointFormat: '{point.x:%Y-%m-%d}: <b>{point.y:.2f}</b>'
|
||||
},
|
||||
yAxis: { title: { text: 'Amount ($)' } },
|
||||
legend: { labelFormatter: function () { return `<span title="${tooltipContent}">${legendName}</span>`; } },
|
||||
tooltip: { pointFormat: '{point.x:%Y-%m-%d}: <b>{point.y:.2f}</b>' },
|
||||
annotations: annotations, // Add annotations
|
||||
series: [{
|
||||
name: legendName,
|
||||
data: chartData,
|
||||
marker: {
|
||||
radius: 2, // Smaller points (default is 4)
|
||||
lineWidth: 0, // Optional: thinner border
|
||||
symbol: 'circle' // Optional: shape of the points (default is 'circle')
|
||||
}
|
||||
}]
|
||||
series: [
|
||||
{ name: legendName, data: chartData, marker: { radius: 2 } }
|
||||
{% if COMP %}
|
||||
,{ name: "TEST", data: compChartData, marker: { radius: 2 } }
|
||||
{% endif %}
|
||||
]
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
||||
Reference in New Issue
Block a user