06/03 Optimize codes, move account action buttons to Accounts page

This commit is contained in:
2026-06-03 10:21:14 -04:00
parent daeea836d0
commit 395a2d9535
4 changed files with 132 additions and 135 deletions
+99 -5
View File
@@ -25,6 +25,7 @@
{% if accounts %}
<div class="row g-3">
{% for acct in accounts %}
{% set ta = teller_map.get(acct.id) %}
<div class="col-12 col-md-6 col-xl-4">
<div class="pcard" style="border-left: 4px solid {{ acct.color }};">
@@ -35,7 +36,12 @@
<i class="bi {{ acct.icon }}"></i>
</div>
<div>
<div style="font-weight:600;font-size:14px;">{{ acct.name }}</div>
<div style="font-weight:600;font-size:14px;">
{{ acct.name }}
{% if ta %}
<span style="font-size:10px;background:#dbeafe;color:#1e40af;border-radius:4px;padding:1px 5px;margin-left:4px;vertical-align:middle;">Teller</span>
{% endif %}
</div>
<div style="font-size:11px;color:var(--muted);">{{ acct.account_type | replace('_',' ') | title }}</div>
</div>
</div>
@@ -56,19 +62,18 @@
</div>
{% if tab == 'bank' %}
<div class="mono {% if acct.balance >= 0 %}text-income{% else %}text-expense{% endif %}" style="font-size:26px;font-weight:700;margin-top:12px;">
<div id="acct-bal-{{ acct.id }}" class="mono {% if acct.balance >= 0 %}text-income{% else %}text-expense{% endif %}" style="font-size:26px;font-weight:700;margin-top:12px;">
{{ acct.balance | currency }}
</div>
{% endif %}
{% if tab == 'credit' %}
<!-- Amount Owed + Monthly Charges -->
{% set owed = [0, -acct.balance] | max %}
{% set charges = monthly_charges.get(acct.id, 0) %}
<div class="d-flex gap-3 mt-3">
<div style="flex:1;background:#fff5f5;border-radius:8px;padding:10px 12px;">
<div style="font-size:10px;color:#991b1b;font-weight:600;text-transform:uppercase;letter-spacing:.04em;margin-bottom:2px;">Amount Owed</div>
<div class="mono {% if owed > 0 %}text-expense{% else %}" style="color:#10b981;{% endif %}font-size:20px;font-weight:700;">
<div id="acct-bal-{{ acct.id }}" class="mono {% if owed > 0 %}text-expense{% else %}" style="color:#10b981;{% endif %}font-size:20px;font-weight:700;">
{% if owed > 0 %}-{% endif %}{{ owed | currency }}
</div>
</div>
@@ -85,7 +90,7 @@
<div style="font-size:12px;color:var(--muted);margin-top:10px;">{{ acct.notes }}</div>
{% endif %}
<!-- Action buttons -->
<!-- Main action buttons -->
{% if tab == 'credit' %}
<div class="d-flex gap-2 mt-3">
<a href="{{ url_for('transactions.transfer', to_account_id=acct.id, description='Credit Card Payment — ' ~ acct.name) }}"
@@ -114,6 +119,37 @@
</div>
{% endif %}
<!-- Teller action buttons (only for accounts linked to Teller) -->
{% if ta %}
<div class="d-flex gap-2 mt-2 pt-2" style="border-top:1px solid var(--border);">
<button id="refbtn-{{ acct.id }}"
onclick="refreshTellerBalance({{ ta.id }}, {{ acct.id }}, {{ 'true' if acct.account_type == 'credit_card' else 'false' }}, this)"
class="btn btn-sm btn-outline-primary flex-fill" style="font-size:11px;"
title="Pull live balance from bank">
<i class="bi bi-arrow-clockwise me-1"></i>Refresh
</button>
<a href="{{ url_for('teller.sync_preview_view', teller_account_id=ta.id) }}"
class="btn btn-sm btn-outline-primary flex-fill" style="font-size:11px;"
title="Sync new transactions from bank">
<i class="bi bi-cloud-download me-1"></i>Sync
</a>
<form method="POST" action="{{ url_for('teller.full_resync', teller_account_id=ta.id) }}" style="flex:1;">
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}">
<input type="hidden" name="next" value="{{ url_for('accounts.index', tab=tab) }}">
<button type="submit" class="btn btn-sm btn-outline-warning w-100" style="font-size:11px;"
title="Re-fetch full 90-day history on next sync"
onclick="return confirm('Reset sync cursor for {{ acct.name }}?\nNext sync will re-fetch last 90 days. Already-imported transactions will be skipped.')">
<i class="bi bi-arrow-counterclockwise me-1"></i>Reset
</button>
</form>
</div>
{% if ta.last_sync_date %}
<div style="font-size:10px;color:var(--muted);margin-top:4px;text-align:right;">
Last synced {{ ta.last_sync_date.strftime('%b %d') }}
</div>
{% endif %}
{% endif %}
</div>
</div>
{% endfor %}
@@ -137,3 +173,61 @@
{% endif %}
{% endblock %}
{% block extra_js %}
<script>
const CSRF = document.querySelector('meta[name="csrf-token"]').content;
function refreshTellerBalance(taId, acctId, isCreditCard, btn) {
btn.disabled = true;
btn.innerHTML = '<span class="spinner-border spinner-border-sm me-1"></span>…';
fetch('/teller/balance/' + taId, {
method: 'POST',
headers: { 'X-CSRFToken': CSRF },
})
.then(r => r.json())
.then(data => {
btn.disabled = false;
btn.innerHTML = '<i class="bi bi-arrow-clockwise me-1"></i>Refresh';
if (data.balance != null || data.available != null) {
const sym = '{{ current_user.currency_symbol }}';
const fmt = v => {
const n = parseFloat(v);
const abs = Math.abs(n).toLocaleString(undefined, {minimumFractionDigits:2, maximumFractionDigits:2});
return (n < 0 ? '-' : '') + sym + abs;
};
const rawVal = data.balance != null ? data.balance : data.available;
const balEl = document.getElementById('acct-bal-' + acctId);
if (balEl) {
if (isCreditCard) {
const owed = Math.max(0, -parseFloat(rawVal));
balEl.textContent = (owed > 0 ? '-' : '') + fmt(owed);
} else {
balEl.textContent = fmt(rawVal);
}
balEl.style.color = '#10b981';
setTimeout(() => balEl.style.color = '', 2500);
}
btn.style.color = '#10b981';
btn.title = 'Balance updated just now';
setTimeout(() => { btn.style.color = ''; btn.title = 'Pull live balance from bank'; }, 3000);
} else {
btn.style.color = '#ef4444';
btn.title = data.error || 'Refresh failed';
setTimeout(() => { btn.style.color = ''; btn.title = 'Pull live balance from bank'; }, 4000);
}
})
.catch(() => {
btn.disabled = false;
btn.innerHTML = '<i class="bi bi-arrow-clockwise me-1"></i>Refresh';
btn.style.color = '#ef4444';
setTimeout(() => { btn.style.color = ''; }, 4000);
});
}
</script>
{% endblock %}