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
+20 -129
View File
@@ -3,15 +3,6 @@
{% block page_title %}Bank Connections{% endblock %}
{% block topbar_actions %}
{% if enrollments %}
<form method="POST" action="{{ url_for('teller.refresh_all_balances') }}" class="d-inline">
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}">
<button type="submit" class="btn btn-sm btn-outline-secondary me-1" style="font-size:12px;"
title="Pull live balances from your bank for all connected accounts">
<i class="bi bi-arrow-clockwise me-1"></i>Refresh All Balances
</button>
</form>
{% endif %}
<button id="tellerConnectBtn" class="btn btn-sm btn-primary" style="font-size:12px;"
{% if not teller_app_id %}title="TELLER_APP_ID not set in .env" style="opacity:.6;"{% endif %}>
<i class="bi bi-bank me-1"></i>Connect a Bank
@@ -29,88 +20,45 @@
<div>
<div style="font-size:15px;font-weight:600;">{{ enrollment.institution_name or 'Unknown Bank' }}</div>
<div style="font-size:12px;color:var(--muted);">
Connected {{ enrollment.created_at.strftime('%b %d, %Y') }} ·
Last synced: {{ enrollment.last_synced_at.strftime('%b %d, %H:%M') if enrollment.last_synced_at else 'Never' }}
Connected {{ enrollment.created_at.strftime('%b %d, %Y') }}
{% if enrollment.last_synced_at %}
· Last synced {{ enrollment.last_synced_at.strftime('%b %d, %H:%M') }}
{% endif %}
</div>
</div>
<form method="POST" action="{{ url_for('teller.disconnect', enrollment_db_id=enrollment.id) }}"
onsubmit="return confirm('Disconnect from {{ enrollment.institution_name }}? Existing transactions are kept.')">
onsubmit="return confirm('Disconnect from {{ enrollment.institution_name }}?\nExisting transactions are kept.')">
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}">
<button type="submit" class="btn btn-sm btn-outline-danger" style="font-size:11px;">Disconnect</button>
<button type="submit" class="btn btn-sm btn-outline-danger" style="font-size:11px;">
<i class="bi bi-x-circle me-1"></i>Disconnect
</button>
</form>
</div>
{% for ta in accounts %}
<div class="d-flex justify-content-between align-items-center py-2" style="border-top:1px solid var(--border);">
<div class="d-flex align-items-center gap-2" style="min-width:0;">
<div style="width:32px;height:32px;border-radius:8px;background:#dbeafe;color:#1e40af;display:flex;align-items:center;justify-content:center;font-size:14px;flex-shrink:0;">
<div class="d-flex align-items-center gap-2">
<div style="width:30px;height:30px;border-radius:8px;background:#dbeafe;color:#1e40af;display:flex;align-items:center;justify-content:center;font-size:13px;flex-shrink:0;">
<i class="bi {% if ta.account_subtype == 'credit_card' %}bi-credit-card{% elif ta.account_subtype == 'savings' %}bi-piggy-bank{% else %}bi-bank{% endif %}"></i>
</div>
<div style="min-width:0;">
<div class="d-flex align-items-center gap-2 flex-wrap">
<span style="font-size:13px;font-weight:500;">{{ ta.account_name }}</span>
{% if ta.pfm_account %}
{# Balance display — updated in-place by refreshBalance() #}
<span id="bal-{{ ta.id }}" class="mono"
style="font-size:13px;font-weight:600;color:var(--text);">
{{ ta.pfm_account.balance | currency }}
</span>
{% if ta.last_sync_date %}
<span style="font-size:10px;color:var(--muted);">
synced {{ ta.last_sync_date.strftime('%b %d') }}
</span>
{% endif %}
{% endif %}
</div>
<div>
<div style="font-size:13px;font-weight:500;">{{ ta.account_name }}</div>
<div style="font-size:11px;color:var(--muted);">
{{ ta.account_subtype | replace('_',' ') | title }}
{% if ta.pfm_account %}
· <span style="color:var(--muted);">{{ ta.pfm_account.name }}</span>
· linked to <span style="color:var(--text);font-weight:500;">{{ ta.pfm_account.name }}</span>
{% else %}
· <span style="color:#f59e0b;">Not mapped</span>
· <span style="color:#f59e0b;font-weight:500;">Not mapped</span>
{% endif %}
</div>
</div>
</div>
<div class="d-flex align-items-center gap-2 flex-shrink-0 ms-2">
{% if ta.pfm_account %}
<button id="refbtn-{{ ta.id }}"
onclick="refreshBalance({{ ta.id }}, this)"
class="btn btn-sm btn-outline-secondary" style="font-size:11px;"
title="Pull live balance from bank">
<i class="bi bi-arrow-clockwise"></i>
</button>
<a href="{{ url_for('teller.sync_preview_view', teller_account_id=ta.id) }}"
class="btn btn-sm btn-outline-primary" style="font-size:11px;">
<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="display:inline;"
onsubmit="return confirm('Reset sync cursor for {{ ta.account_name }}?\nThe next Sync will re-fetch the last 90 days. Already-imported transactions will be skipped.')">
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}">
<button type="submit" class="btn btn-sm btn-outline-warning" style="font-size:11px;" title="Re-fetch full 90-day history on next sync">
<i class="bi bi-arrow-counterclockwise"></i>
</button>
</form>
{% else %}
<a href="{{ url_for('teller.map_accounts', enrollment_id=enrollment.enrollment_id) }}"
class="btn btn-sm btn-outline-warning" style="font-size:11px;">Map Account</a>
{% endif %}
</div>
{% if not ta.pfm_account %}
<a href="{{ url_for('teller.map_accounts', enrollment_id=enrollment.enrollment_id) }}"
class="btn btn-sm btn-outline-warning" style="font-size:11px;">Map Account</a>
{% endif %}
</div>
{% endfor %}
<!-- Sync all -->
{% if accounts | selectattr('pfm_account_id') | list %}
<div class="mt-3 text-end">
<form method="POST" action="{{ url_for('teller.sync_all') }}" style="display:inline;">
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}">
<button type="submit" class="btn btn-sm btn-primary" style="font-size:12px;">
<i class="bi bi-cloud-download me-1"></i>Sync All Accounts
</button>
</form>
</div>
{% endif %}
</div>
</div>
{% endfor %}
@@ -121,6 +69,7 @@
<h5 class="mt-3 mb-1">No banks connected</h5>
<p class="text-muted small mb-3">
Connect your US bank accounts to automatically sync transactions and balances.
Once connected, use the <strong>Accounts</strong> page to sync transactions and refresh balances.
</p>
<button id="tellerConnectBtn2" class="btn btn-primary">
<i class="bi bi-bank me-1"></i>Connect a Bank
@@ -178,7 +127,6 @@ function initTellerConnect(btn) {
'Content-Type': 'application/json',
'X-CSRFToken': CSRF,
},
// Send full Teller payload: { accessToken, user, enrollment }
body: JSON.stringify(enrollment),
})
.then(r => r.json())
@@ -197,9 +145,7 @@ function initTellerConnect(btn) {
btn.innerHTML = '<i class="bi bi-bank me-1"></i>Connect a Bank';
});
},
onExit: function() {
console.log('Teller Connect closed');
},
onExit: function() {},
onFailure: function(error) {
alert('Connection failed: ' + error.message);
},
@@ -210,60 +156,5 @@ function initTellerConnect(btn) {
initTellerConnect(document.getElementById('tellerConnectBtn'));
initTellerConnect(document.getElementById('tellerConnectBtn2'));
function refreshBalance(taId, btn) {
btn.disabled = true;
btn.innerHTML = '<span class="spinner-border spinner-border-sm"></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"></i>';
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;
};
// 'balance' is the correct value for the account type (ledger for credit cards,
// available for bank accounts). Fall back to 'available' for older responses.
const displayVal = data.balance != null ? data.balance : data.available;
// Update inline balance display
const balEl = document.getElementById('bal-' + taId);
if (balEl) {
balEl.textContent = fmt(displayVal);
balEl.style.color = displayVal < 0 ? '#ef4444' : '#10b981';
setTimeout(() => balEl.style.color = '', 2500);
}
// Show available vs ledger in button tooltip
const ledgerNote = (data.ledger !== data.available)
? ` · Ledger: ${fmt(data.ledger)}`
: '';
btn.title = `Available: ${fmt(data.available)}${ledgerNote} — updated just now`;
btn.style.color = '#10b981';
setTimeout(() => { btn.style.color = ''; btn.title = 'Pull live balance from bank'; }, 3000);
} else {
btn.title = data.error || 'Refresh failed';
btn.style.color = '#ef4444';
setTimeout(() => { btn.style.color = ''; btn.title = 'Pull live balance from bank'; }, 4000);
}
})
.catch(() => {
btn.disabled = false;
btn.innerHTML = '<i class="bi bi-arrow-clockwise"></i>';
btn.title = 'Network error — try again';
btn.style.color = '#ef4444';
setTimeout(() => { btn.style.color = ''; btn.title = 'Pull live balance from bank'; }, 4000);
});
}
</script>
{% endblock %}