Files
Personal-Finance-Management/app/templates/settings/index.html
T
2026-06-03 16:06:30 -04:00

172 lines
9.2 KiB
HTML

{% extends "base.html" %}
{% block title %}Settings{% endblock %}
{% block page_title %}Settings{% endblock %}
{% block content %}
<div class="row g-3">
<!-- Nav cards -->
<div class="col-12 col-sm-6 col-lg-3">
<a href="{{ url_for('teller.index') }}" class="text-decoration-none">
<div class="pcard text-center py-4" style="transition:all .15s;" onmouseover="this.style.borderColor='#10b981'" onmouseout="this.style.borderColor='var(--border)'">
<i class="bi bi-bank2" style="font-size:2rem;color:#10b981;"></i>
<div style="font-size:14px;font-weight:600;margin-top:10px;">Teller Sync</div>
<div style="font-size:12px;color:var(--muted);margin-top:4px;">Connect US bank accounts via Teller</div>
</div>
</a>
</div>
<div class="col-12 col-sm-6 col-lg-3">
<a href="{{ url_for('schwab.index') }}" class="text-decoration-none">
<div class="pcard text-center py-4" style="transition:all .15s;" onmouseover="this.style.borderColor='#3b82f6'" onmouseout="this.style.borderColor='var(--border)'">
<i class="bi bi-bank" style="font-size:2rem;color:#3b82f6;"></i>
<div style="font-size:14px;font-weight:600;margin-top:10px;">Schwab Sync</div>
<div style="font-size:12px;color:var(--muted);margin-top:4px;">Connect Charles Schwab directly</div>
</div>
</a>
</div>
<div class="col-12 col-sm-6 col-lg-3">
<a href="{{ url_for('settings.profile') }}" class="text-decoration-none">
<div class="pcard text-center py-4" style="transition:all .15s;" onmouseover="this.style.borderColor='#3b82f6'" onmouseout="this.style.borderColor='var(--border)'">
<i class="bi bi-person-circle" style="font-size:2rem;color:#3b82f6;"></i>
<div style="font-size:14px;font-weight:600;margin-top:10px;">Profile</div>
<div style="font-size:12px;color:var(--muted);margin-top:4px;">Name, currency, timezone, AI model</div>
</div>
</a>
</div>
<div class="col-12 col-sm-6 col-lg-3">
<a href="{{ url_for('settings.recurring') }}" class="text-decoration-none">
<div class="pcard text-center py-4" style="transition:all .15s;" onmouseover="this.style.borderColor='#10b981'" onmouseout="this.style.borderColor='var(--border)'">
<i class="bi bi-repeat" style="font-size:2rem;color:#10b981;"></i>
<div style="font-size:14px;font-weight:600;margin-top:10px;">Recurring</div>
<div style="font-size:12px;color:var(--muted);margin-top:4px;">Automate recurring income & expenses</div>
</div>
</a>
</div>
<div class="col-12 col-sm-6 col-lg-3">
<a href="{{ url_for('settings.import_csv') }}" class="text-decoration-none">
<div class="pcard text-center py-4" style="transition:all .15s;" onmouseover="this.style.borderColor='#f59e0b'" onmouseout="this.style.borderColor='var(--border)'">
<i class="bi bi-upload" style="font-size:2rem;color:#f59e0b;"></i>
<div style="font-size:14px;font-weight:600;margin-top:10px;">Import</div>
<div style="font-size:12px;color:var(--muted);margin-top:4px;">Bulk import from CSV file</div>
</div>
</a>
</div>
<div class="col-12 col-sm-6 col-lg-3">
<a href="{{ url_for('settings.password') }}" class="text-decoration-none">
<div class="pcard text-center py-4" style="transition:all .15s;" onmouseover="this.style.borderColor='#8b5cf6'" onmouseout="this.style.borderColor='var(--border)'">
<i class="bi bi-shield-lock" style="font-size:2rem;color:#8b5cf6;"></i>
<div style="font-size:14px;font-weight:600;margin-top:10px;">Security</div>
<div style="font-size:12px;color:var(--muted);margin-top:4px;">Change password</div>
</div>
</a>
</div>
</div>
<!-- Two-Factor Authentication -->
<div class="row g-3 mt-1">
<div class="col-12 col-md-6">
<div class="pcard h-100">
<div class="d-flex align-items-center gap-2 mb-3">
<i class="bi bi-shield-lock-fill" style="font-size:1.3rem;color:#5b21b6;"></i>
<span class="pcard-title mb-0">Two-Factor Authentication (2FA)</span>
{% if current_user.totp_enabled %}
<span class="badge ms-auto" style="background:#d1fae5;color:#065f46;font-size:11px;">Enabled</span>
{% else %}
<span class="badge ms-auto" style="background:#fee2e2;color:#991b1b;font-size:11px;">Disabled</span>
{% endif %}
</div>
{% if current_user.totp_enabled %}
<p style="font-size:13px;color:var(--muted);">
2FA is active. Each login requires a 6-digit code from your authenticator app.
</p>
<form method="POST" action="{{ url_for('auth.totp_disable') }}"
onsubmit="return confirm('Disable two-factor authentication?\nYou will only need a password to log in.')">
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}">
<div class="mb-2">
<input type="password" name="password" class="form-control form-control-sm"
placeholder="Confirm your password" required style="max-width:280px;">
</div>
<button type="submit" class="btn btn-sm btn-outline-danger">
<i class="bi bi-shield-x me-1"></i>Disable 2FA
</button>
</form>
{% else %}
<p style="font-size:13px;color:var(--muted);">
Protect your account with a time-based one-time password (TOTP).
Use Google Authenticator, Authy, or any compatible app.
</p>
<a href="{{ url_for('auth.totp_setup') }}" class="btn btn-sm btn-outline-primary">
<i class="bi bi-shield-plus me-1"></i>Enable 2FA
</a>
{% endif %}
</div>
</div>
</div>
<!-- Maintenance + Email Alerts -->
<div class="row g-3 mt-1">
<div class="col-12 col-md-6">
<div class="pcard h-100">
<div class="pcard-title mb-3">Maintenance</div>
<p style="font-size:13px;color:var(--muted);">
Recalculate every account balance from scratch using all recorded transactions.
Run this after bulk imports or manual adjustments to make sure balances are correct.
</p>
<form method="POST" action="{{ url_for('settings.recalc_balances') }}"
onsubmit="return confirm('Recalculate all account balances now?')">
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}">
<button type="submit" class="btn btn-sm btn-outline-primary">
<i class="bi bi-arrow-repeat me-1"></i>Recalculate All Balances
</button>
</form>
</div>
</div>
<div class="col-12 col-md-6">
<div class="pcard h-100">
<div class="pcard-title mb-3">Budget Alert Emails</div>
{% if smtp_ok %}
<div class="d-flex align-items-center gap-2 mb-2">
<i class="bi bi-check-circle-fill text-success"></i>
<span style="font-size:13px;">Email alerts enabled → <strong>{{ alert_email }}</strong></span>
</div>
<p style="font-size:12px;color:var(--muted);">
An email is sent automatically when a budget category hits 80% or 100% of its monthly limit.
</p>
{% else %}
<div class="d-flex align-items-center gap-2 mb-2">
<i class="bi bi-x-circle-fill text-danger"></i>
<span style="font-size:13px;color:var(--muted);">Email alerts not configured</span>
</div>
<p style="font-size:12px;color:var(--muted);">
Add these to your <code>.env</code> file to enable budget alert emails:
</p>
<pre style="font-size:11px;background:#f8fafc;padding:10px;border-radius:6px;margin:0;overflow-x:auto;">SMTP_HOST=smtp.gmail.com
SMTP_PORT=587
SMTP_USER=you@gmail.com
SMTP_PASSWORD=your-app-password
ALERT_EMAIL=you@gmail.com</pre>
{% endif %}
</div>
</div>
</div>
<!-- Upcoming recurring -->
{% if upcoming %}
<div class="pcard mt-4">
<div class="pcard-title mb-3">Upcoming Recurring (next 30 days)</div>
<table class="pfm-table">
<thead><tr><th>Date</th><th>Description</th><th>Type</th><th class="text-end">Amount</th></tr></thead>
<tbody>
{% for item in upcoming[:10] %}
<tr>
<td style="font-size:12px;color:var(--muted);white-space:nowrap;">{{ item.date.strftime('%b %d') }}</td>
<td style="font-size:13px;">{{ item.description }}</td>
<td><span class="badge {% if item.type == 'income' %}badge-income{% else %}badge-expense{% endif %}" style="font-size:11px;">{{ item.type | title }}</span></td>
<td class="text-end mono {% if item.type == 'income' %}text-income{% else %}text-expense{% endif %}" style="font-size:13px;">{{ item.amount | currency }}</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
{% endif %}
{% endblock %}