72 lines
2.0 KiB
HTML
72 lines
2.0 KiB
HTML
{% extends "base.html" %}
|
|
{% block content %}
|
|
<script src="https://cdn.jsdelivr.net/npm/chart.js@4.4.0/dist/chart.umd.min.js"></script>
|
|
<div class="page-header">
|
|
<h1>Spending Report</h1>
|
|
</div>
|
|
|
|
{% if by_category %}
|
|
|
|
<!-- Horizontal bar chart -->
|
|
<div class="card-wide" style="margin-bottom:1.5rem;">
|
|
<canvas id="spending-bar-chart" height="80"></canvas>
|
|
</div>
|
|
|
|
<div class="summary-grid">
|
|
{% for cat, data in by_category | dictsort %}
|
|
<div class="summary-card" style="border-top: 4px solid {{ colors.get(cat, '#ddd') }};">
|
|
<div class="summary-cat-name">{{ cat }}</div>
|
|
<div class="summary-cat-total">${{ data.total | money }}</div>
|
|
{% if data.subcategories %}
|
|
<ul class="subcat-list">
|
|
{% for sub, amt in data.subcategories | dictsort %}
|
|
<li>
|
|
<span>{{ sub }}</span>
|
|
<span>${{ amt | money }}</span>
|
|
</li>
|
|
{% endfor %}
|
|
</ul>
|
|
{% endif %}
|
|
</div>
|
|
{% endfor %}
|
|
</div>
|
|
{% else %}
|
|
<div class="empty-state">
|
|
<p>No categorized transactions yet. <a href="{{ url_for('import_transactions') }}">Import a statement</a> to get started.</p>
|
|
</div>
|
|
{% endif %}
|
|
|
|
<script>
|
|
{% if by_category %}
|
|
const spendingLabels = {{ by_category.keys() | list | tojson }};
|
|
const spendingValues = {{ by_category.values() | map(attribute='total') | list | tojson }};
|
|
const spendingColors = [{% for cat in by_category %}'{{ colors.get(cat, "#ddd") }}',{% endfor %}];
|
|
|
|
new Chart(document.getElementById('spending-bar-chart'), {
|
|
type: 'bar',
|
|
data: {
|
|
labels: spendingLabels,
|
|
datasets: [{
|
|
data: spendingValues,
|
|
backgroundColor: spendingColors,
|
|
borderRadius: 6,
|
|
borderSkipped: false,
|
|
}]
|
|
},
|
|
options: {
|
|
indexAxis: 'y',
|
|
plugins: {
|
|
legend: { display: false },
|
|
tooltip: { callbacks: { label: ctx => ' $' + ctx.parsed.x.toFixed(2) } }
|
|
},
|
|
scales: {
|
|
x: { grid: { color: '#f0ece6' }, ticks: { callback: v => '$' + v.toLocaleString() } },
|
|
y: { grid: { display: false } }
|
|
},
|
|
animation: { duration: 400 },
|
|
}
|
|
});
|
|
{% endif %}
|
|
</script>
|
|
{% endblock %}
|