245 lines
9.8 KiB
HTML
245 lines
9.8 KiB
HTML
{% extends "base.html" %}
|
||
{% block content %}
|
||
<div class="page-header">
|
||
<h1>{{ month_name }} {{ year }} — Monthly Summary</h1>
|
||
<div class="month-nav">
|
||
<a href="{{ url_for('month_summary', year=prev_year, month=prev_month) }}" class="btn btn-ghost">‹ Prev</a>
|
||
<a href="{{ url_for('month_summary', year=next_year, month=next_month) }}" class="btn btn-ghost">Next ›</a>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Top-line summary cards -->
|
||
<div class="card-grid" style="margin-bottom:1.5rem;">
|
||
<div class="card">
|
||
<div class="card-label">Total Income</div>
|
||
<div class="card-value" style="color:#5A9E68;">${{ total_income | money }}</div>
|
||
</div>
|
||
<div class="card">
|
||
<div class="card-label">Total Spent</div>
|
||
<div class="card-value">${{ (total_spending + total_bills) | money }}</div>
|
||
</div>
|
||
<div class="card">
|
||
<div class="card-label">Saved to Funds</div>
|
||
<div class="card-value">${{ total_sinking | money }}</div>
|
||
</div>
|
||
<div class="card" style="border: 2px solid {% if net >= 0 %}#A8C5A0{% else %}#C57A7A{% endif %};">
|
||
<div class="card-label">{{ 'Left Over' if net >= 0 else 'Over Budget' }}</div>
|
||
<div class="card-value" style="color:{% if net >= 0 %}#5A9E68{% else %}#c07070{% endif %};">
|
||
${{ net | abs | money }}
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Cash flow bar -->
|
||
{% if total_income > 0 %}
|
||
{% set spent_pct = [(total_spending + total_bills) / total_income * 100, 100] | min | int %}
|
||
{% set sinking_pct = [total_sinking / total_income * 100, 100 - spent_pct] | min | int %}
|
||
<div style="margin-bottom:2rem;">
|
||
<div style="display:flex;justify-content:space-between;font-size:0.82rem;color:var(--text-mid);margin-bottom:0.35rem;">
|
||
<span>Where did the money go?</span>
|
||
<span>${{ total_income | money }} income</span>
|
||
</div>
|
||
<div style="display:flex;height:18px;border-radius:999px;overflow:hidden;background:#EDE8E2;">
|
||
<div style="width:{{ spent_pct }}%;background:#C57A7A;" title="Spent"></div>
|
||
<div style="width:{{ sinking_pct }}%;background:#A8C5A0;" title="Savings"></div>
|
||
</div>
|
||
<div style="display:flex;gap:1.25rem;font-size:0.78rem;color:var(--text-mid);margin-top:0.4rem;">
|
||
<span><span style="display:inline-block;width:10px;height:10px;border-radius:50%;background:#C57A7A;margin-right:3px;"></span>Spent {{ spent_pct }}%</span>
|
||
<span><span style="display:inline-block;width:10px;height:10px;border-radius:50%;background:#A8C5A0;margin-right:3px;"></span>Saved {{ sinking_pct }}%</span>
|
||
{% if net > 0 %}
|
||
<span><span style="display:inline-block;width:10px;height:10px;border-radius:50%;background:#EDE8E2;border:1px solid #ccc;margin-right:3px;"></span>Left over {{ [100 - spent_pct - sinking_pct, 0] | max }}%</span>
|
||
{% endif %}
|
||
</div>
|
||
</div>
|
||
{% endif %}
|
||
|
||
<div style="display:grid;grid-template-columns:1fr 1fr;gap:1.5rem;">
|
||
|
||
<!-- Left column -->
|
||
<div>
|
||
|
||
<!-- Income -->
|
||
<div class="budget-section">
|
||
<div class="budget-section-header" style="border-left-color:#7AB87A;">
|
||
<h3>Income</h3>
|
||
<a href="{{ url_for('income_view', year=year, month=month) }}" class="edit-link">manage</a>
|
||
</div>
|
||
{% if income_by_stream %}
|
||
<table class="budget-table">
|
||
<tbody>
|
||
{% for s in income_streams %}
|
||
{% set amt = income_by_stream.get(s.name, 0) %}
|
||
{% if amt > 0 %}
|
||
<tr>
|
||
<td>{{ s.name }}</td>
|
||
<td><span class="owner-tag" style="background:{{ owner_colors.get(s.owner,'#ddd') }};font-size:0.68rem;">{{ s.owner }}</span></td>
|
||
<td class="amount">${{ amt | money }}</td>
|
||
</tr>
|
||
{% endif %}
|
||
{% endfor %}
|
||
</tbody>
|
||
<tfoot>
|
||
<tr class="total-row">
|
||
<td colspan="2">Total</td>
|
||
<td class="amount">${{ total_income | money }}</td>
|
||
</tr>
|
||
</tfoot>
|
||
</table>
|
||
{% else %}
|
||
<p class="muted" style="padding:0.75rem 0;">No income logged yet. <a href="{{ url_for('income_view', year=year, month=month) }}">Log income →</a></p>
|
||
{% endif %}
|
||
</div>
|
||
|
||
<!-- Bills -->
|
||
<div class="budget-section" style="margin-top:1.25rem;">
|
||
<div class="budget-section-header" style="border-left-color:#C57A7A;">
|
||
<h3>Bills</h3>
|
||
<a href="{{ url_for('bills_list') }}" class="edit-link">manage</a>
|
||
</div>
|
||
{% if monthly_bills %}
|
||
<table class="budget-table">
|
||
<tbody>
|
||
{% for b in monthly_bills %}
|
||
<tr>
|
||
<td>{{ b.name }}</td>
|
||
<td class="muted">day {{ b.due_day }}</td>
|
||
<td class="amount">{% if b.amount %}${{ b.amount | money }}{% else %}<span class="muted">—</span>{% endif %}</td>
|
||
</tr>
|
||
{% endfor %}
|
||
</tbody>
|
||
<tfoot>
|
||
<tr class="total-row">
|
||
<td colspan="2">Total</td>
|
||
<td class="amount">${{ total_bills | money }}</td>
|
||
</tr>
|
||
</tfoot>
|
||
</table>
|
||
{% else %}
|
||
<p class="muted" style="padding:0.75rem 0;">No bills this month.</p>
|
||
{% endif %}
|
||
</div>
|
||
|
||
<!-- Sinking Funds -->
|
||
{% if sinking_contributions %}
|
||
<div class="budget-section" style="margin-top:1.25rem;">
|
||
<div class="budget-section-header" style="border-left-color:#C5BBA0;">
|
||
<h3>Sinking Fund Contributions</h3>
|
||
<a href="{{ url_for('sinking_view') }}" class="edit-link">manage</a>
|
||
</div>
|
||
<table class="budget-table">
|
||
<tbody>
|
||
{% for name, amt in sinking_contributions.items() %}
|
||
<tr>
|
||
<td>{{ name }}</td>
|
||
<td class="amount">${{ amt | money }}</td>
|
||
</tr>
|
||
{% endfor %}
|
||
</tbody>
|
||
<tfoot>
|
||
<tr class="total-row">
|
||
<td>Total Saved</td>
|
||
<td class="amount">${{ total_sinking | money }}</td>
|
||
</tr>
|
||
</tfoot>
|
||
</table>
|
||
</div>
|
||
{% endif %}
|
||
|
||
</div>
|
||
|
||
<!-- Right column -->
|
||
<div>
|
||
|
||
<!-- Spending by category -->
|
||
<div class="budget-section">
|
||
<div class="budget-section-header" style="border-left-color:#D4A96A;">
|
||
<h3>Spending by Category</h3>
|
||
<a href="{{ url_for('transactions_view') }}" class="edit-link">view transactions</a>
|
||
</div>
|
||
{% if spending_by_cat %}
|
||
{% set max_spend = spending_by_cat.values() | max %}
|
||
<table class="budget-table">
|
||
<thead><tr><th>Category</th><th>Budgeted</th><th>Actual</th><th></th></tr></thead>
|
||
<tbody>
|
||
{% for cat, amt in spending_by_cat | dictsort(by='value', reverse=true) %}
|
||
{% set budgeted = budgeted_by_cat.get(cat, 0) %}
|
||
{% set over = budgeted > 0 and amt > budgeted %}
|
||
<tr{% if over %} style="background:#fdf0f0;"{% endif %}>
|
||
<td><span class="tag" style="background:{{ colors.get(cat,'#ddd') }};">{{ cat }}</span></td>
|
||
<td class="amount muted">{% if budgeted %}${{ budgeted | money }}{% else %}<span style="color:#ccc;">—</span>{% endif %}</td>
|
||
<td class="amount">
|
||
${{ amt | money }}
|
||
{% if over %}<span style="font-size:0.7rem;color:#c07070;"> over</span>{% endif %}
|
||
</td>
|
||
<td style="width:30%;padding-right:0.5rem;">
|
||
<div style="background:#EDE8E2;border-radius:4px;height:8px;">
|
||
<div style="background:{{ colors.get(cat,'#bbb') }};border-radius:4px;height:8px;width:{{ [(amt / max_spend * 100)|int, 100]|min }}%;"></div>
|
||
</div>
|
||
</td>
|
||
</tr>
|
||
{% endfor %}
|
||
</tbody>
|
||
<tfoot>
|
||
<tr class="total-row">
|
||
<td>Total</td>
|
||
<td class="amount muted">{% if budgeted_by_cat %}${{ budgeted_by_cat.values( | money|sum) }}{% endif %}</td>
|
||
<td class="amount">${{ total_spending | money }}</td>
|
||
<td></td>
|
||
</tr>
|
||
</tfoot>
|
||
</table>
|
||
{% else %}
|
||
<p class="muted" style="padding:0.75rem 0;">No categorized transactions yet. <a href="{{ url_for('review') }}">Review transactions →</a></p>
|
||
{% endif %}
|
||
</div>
|
||
|
||
<!-- Debt payments -->
|
||
{% if debt_payments %}
|
||
<div class="budget-section" style="margin-top:1.25rem;">
|
||
<div class="budget-section-header" style="border-left-color:#C57A7A;">
|
||
<h3>Debt Payments</h3>
|
||
<a href="{{ url_for('debts_view', year=year, month=month) }}" class="edit-link">manage</a>
|
||
</div>
|
||
<table class="budget-table">
|
||
<thead><tr><th>Debt</th><th>Owner</th><th>Payment</th><th>Extra</th></tr></thead>
|
||
<tbody>
|
||
{% for d in debt_payments %}
|
||
<tr>
|
||
<td>{{ d.name }}</td>
|
||
<td><span class="owner-tag" style="background:{{ owner_colors.get(d.owner,'#ddd') }};font-size:0.68rem;">{{ d.owner }}</span></td>
|
||
<td class="amount">${{ d.payment | money }}</td>
|
||
<td class="amount">{% if d.extra > 0 %}<span style="color:#5A9E68;">+${{ d.extra | money }}</span>{% else %}<span class="muted">—</span>{% endif %}</td>
|
||
</tr>
|
||
{% endfor %}
|
||
</tbody>
|
||
<tfoot>
|
||
<tr class="total-row">
|
||
<td colspan="2">Total Paid</td>
|
||
<td class="amount">${{ total_debt_paid | money }}</td>
|
||
<td class="amount" style="color:#5A9E68;">{% if total_extra_paid > 0 %}+${{ total_extra_paid | money }} extra{% endif %}</td>
|
||
</tr>
|
||
</tfoot>
|
||
</table>
|
||
</div>
|
||
{% endif %}
|
||
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Bottom net summary -->
|
||
<div class="leftover-bar {% if net < 0 %}leftover-negative{% endif %}" style="margin-top:2rem;">
|
||
<div>
|
||
<div style="font-weight:600;font-size:1rem;">{{ month_name }} {{ year }} — Final Summary</div>
|
||
<div style="font-size:0.82rem;color:var(--text-light);margin-top:0.2rem;">
|
||
${{ "%.2f"|format(total_income) }} income
|
||
− ${{ "%.2f"|format(total_spending + total_bills) }} spent
|
||
− ${{ "%.2f"|format(total_sinking) }} saved
|
||
</div>
|
||
</div>
|
||
<div class="leftover-amount">
|
||
{% if net >= 0 %}+{% endif %}${{ net | money }}
|
||
</div>
|
||
</div>
|
||
|
||
{% endblock %}
|