Files
Moon-Household-Budget/templates/debts.html
2026-06-06 14:22:57 -05:00

775 lines
36 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

{% extends "base.html" %}
{% block content %}
<div class="page-header">
<h1>Debt Tracker — {{ month_name }} {{ year }}</h1>
<div class="month-nav">
<a href="{{ url_for('debts_view', year=prev_year, month=prev_month) }}" class="btn btn-ghost"> Prev</a>
<a href="{{ url_for('debts_view', year=next_year, month=next_month) }}" class="btn btn-ghost">Next </a>
<a href="{{ url_for('debt_strategy') }}" class="btn" style="background:#C47A4A;color:#fff;border:none;">📊 Debt Payoff Strategy</a>
</div>
</div>
<!-- Add Forms -->
<div style="display:flex;gap:0.75rem;margin-bottom:1.5rem;flex-wrap:wrap;">
<details class="add-debt-details">
<summary class="btn btn-primary" style="display:inline-block;cursor:pointer;">+ Add Credit Card</summary>
<div class="card-wide" style="margin-top:0.75rem;">
<form method="POST" action="{{ url_for('debt_add') }}" class="bill-form">
<input type="hidden" name="debt_category" value="Credit Cards">
<div class="bill-form-grid">
<div class="form-group">
<label>Card Name</label>
<input type="text" name="name" placeholder="e.g. Discover, Chase, Indigo" required>
</div>
<div class="form-group">
<label>Owner</label>
<select name="owner">
<option value="Bonna">Bonna</option>
<option value="Tony">Tony</option>
<option value="Both">Both</option>
</select>
</div>
<div class="form-group">
<label>Current Balance</label>
<input type="number" name="current_balance" step="0.01" min="0" placeholder="0.00" required>
</div>
<div class="form-group">
<label>APR % <span class="optional">(leave blank if unknown)</span></label>
<input type="number" name="apr" step="0.01" min="0" max="100" placeholder="e.g. 24.99">
</div>
<div class="form-group">
<label>Minimum Payment</label>
<input type="number" name="minimum" step="0.01" min="0" placeholder="0.00">
</div>
<div class="form-group">
<label>Account Name in App <span class="optional">(to match transactions)</span></label>
<input type="text" name="account_match" placeholder="e.g. Discover">
</div>
<div class="form-group">
<label>Notes <span class="optional">(optional)</span></label>
<input type="text" name="notes" placeholder="e.g. balance transfer, 0% promo ends Aug">
</div>
</div>
<button type="submit" class="btn btn-primary">Add Debt</button>
</form>
</div>
</details>
<details class="add-debt-details">
<summary class="btn btn-dusty-rose" style="display:inline-block;cursor:pointer;">+ Add Medical Debt</summary>
<div class="card-wide" style="margin-top:0.75rem;">
<form method="POST" action="{{ url_for('debt_add') }}" class="bill-form">
<input type="hidden" name="debt_category" value="Medical Payment Plans">
<div class="bill-form-grid">
<div class="form-group">
<label>Provider / Institution</label>
<input type="text" name="name" placeholder="e.g. Marshfield Clinic, Oakleaf" required>
</div>
<div class="form-group">
<label>Owner</label>
<select name="owner">
<option value="Bonna">Bonna</option>
<option value="Tony">Tony</option>
<option value="Both">Both</option>
</select>
</div>
<div class="form-group">
<label>Current Balance</label>
<input type="number" name="current_balance" step="0.01" min="0" placeholder="0.00" required>
</div>
<div class="form-group">
<label>APR % <span class="optional">(0% if interest-free plan)</span></label>
<input type="number" name="apr" step="0.01" min="0" max="100" placeholder="0.00">
</div>
<div class="form-group">
<label>Monthly Payment</label>
<input type="number" name="minimum" step="0.01" min="0" placeholder="0.00">
</div>
<div class="form-group">
<label>Account Name in App <span class="optional">(optional)</span></label>
<input type="text" name="account_match" placeholder="e.g. Marshfield">
</div>
<div class="form-group">
<label>Notes <span class="optional">(optional)</span></label>
<input type="text" name="notes" placeholder="e.g. interest-free, started Jan 2026">
</div>
</div>
<button type="submit" class="btn btn-primary">Add Medical Debt</button>
</form>
</div>
</details>
<details class="add-debt-details">
<summary class="btn btn-powder-blue" style="display:inline-block;cursor:pointer;">+ Add Affirm / Klarna / BNPL Plan</summary>
<div class="card-wide" style="margin-top:0.75rem;">
<form method="POST" action="{{ url_for('bnpl_add') }}" class="bill-form">
<div class="bill-form-grid">
<div class="form-group">
<label>Provider</label>
<select name="bnpl_provider">
<option>Affirm</option>
<option>Klarna</option>
<option>Afterpay</option>
<option>Zip</option>
<option>Other</option>
</select>
</div>
<div class="form-group">
<label>What was it for?</label>
<input type="text" name="description" placeholder="e.g. Living room couch" required>
</div>
<div class="form-group">
<label>Merchant / Store</label>
<input type="text" name="merchant" placeholder="e.g. Wayfair, Amazon">
</div>
<div class="form-group">
<label>Owner</label>
<select name="owner">
<option value="Bonna">Bonna</option>
<option value="Tony">Tony</option>
<option value="Both">Both</option>
</select>
</div>
<div class="form-group">
<label>Original Purchase Amount</label>
<input type="number" name="original_amount" step="0.01" min="0" placeholder="0.00">
</div>
<div class="form-group">
<label>Payment Amount</label>
<input type="number" name="payment_amount" step="0.01" min="0" placeholder="0.00" required>
</div>
<div class="form-group">
<label>Total Number of Payments</label>
<input type="number" name="total_payments" min="1" placeholder="e.g. 4 or 6 or 12" required>
</div>
<div class="form-group">
<label>Payments Already Made</label>
<input type="number" name="payments_made" min="0" value="0">
</div>
<div class="form-group">
<label>Payment Frequency</label>
<select name="frequency">
<option value="biweekly">Every 2 weeks</option>
<option value="monthly">Monthly</option>
<option value="4-payment">4 payments (every 2 weeks)</option>
</select>
</div>
<div class="form-group">
<label>Next Payment Date</label>
<input type="date" name="next_payment_date" required>
</div>
<div class="form-group">
<label>Notes <span class="optional">(optional)</span></label>
<input type="text" name="notes" placeholder="e.g. for bedroom, holiday gift">
</div>
</div>
<button type="submit" class="btn btn-primary">Add BNPL Plan</button>
</form>
</div>
</details>
<details class="add-debt-details">
<summary class="btn btn-moss" style="display:inline-block;cursor:pointer;">📋 Add Card by Pasting Statement</summary>
<div class="card-wide" style="margin-top:0.75rem;">
<p class="muted" style="font-size:0.85rem;margin-bottom:1rem;">Fill in the card name and owner, paste your statement text, and we'll create the card and fill in all the numbers at once.</p>
<div style="display:grid;grid-template-columns:1fr 1fr;gap:1rem;margin-bottom:1rem;">
<div class="form-group">
<label>Card Name</label>
<input type="text" id="paste-card-name" placeholder="e.g. Self Visa, Discover" class="budget-input" style="width:100%;">
</div>
<div class="form-group">
<label>Owner</label>
<select id="paste-card-owner" class="budget-input" style="width:100%;">
<option value="Bonna">Bonna</option>
<option value="Tony">Tony</option>
<option value="Both">Both</option>
</select>
</div>
</div>
<div class="form-group">
<label>Paste Statement Text</label>
<textarea id="paste-new-statement" style="width:100%;height:160px;border:1px solid var(--border);border-radius:8px;padding:0.75rem;font-size:0.82rem;resize:vertical;" placeholder="Copy all the text from your statement and paste it here…"></textarea>
</div>
<div id="paste-new-result" style="font-size:0.85rem;margin-bottom:0.75rem;"></div>
<button class="btn btn-primary" onclick="addCardFromStatement()">Add Card & Import Statement</button>
</div>
</details>
</div>
<!-- Debt Summary Breakdown -->
<div class="debt-summary-block">
<table class="debt-summary-table">
<thead>
<tr>
<th>Category</th>
<th>Balance</th>
<th>Minimums</th>
<th>Paid This Month</th>
<th>Extra</th>
</tr>
</thead>
<tbody>
{% for cat in debt_categories %}
{% set ct = totals[cat] %}
{% if by_category[cat] %}
<tr>
<td>{{ cat }}</td>
<td class="amount">${{ ct.balance | money }}</td>
<td class="amount">${{ ct.minimum | money }}</td>
<td class="amount">${{ ct.paid | money }}</td>
<td class="amount accent">${{ ct.extra | money }}</td>
</tr>
{% endif %}
{% endfor %}
</tbody>
<tfoot>
<tr class="total-row">
<td><strong>Grand Total</strong></td>
<td class="amount"><strong>${{ grand.balance | money }}</strong></td>
<td class="amount"><strong>${{ grand.minimum | money }}</strong></td>
<td class="amount"><strong>${{ grand.paid | money }}</strong></td>
<td class="amount accent"><strong>${{ grand.extra | money }}</strong></td>
</tr>
</tfoot>
</table>
</div>
<!-- Debt-to-Income Ratio -->
{% if dti is not none %}
{% if dti <= 20 %}
{% set dti_color = "#5A9E68" %}{% set dti_label = "Healthy" %}{% set dti_tip = "Under 20% is excellent." %}
{% elif dti <= 36 %}
{% set dti_color = "#D4A96A" %}{% set dti_label = "Manageable" %}{% set dti_tip = "2036% is average. Keep an eye on it." %}
{% elif dti <= 50 %}
{% set dti_color = "#E8956A" %}{% set dti_label = "High" %}{% set dti_tip = "3650% is high. Prioritize paying down debt." %}
{% else %}
{% set dti_color = "#C57A7A" %}{% set dti_label = "Critical" %}{% set dti_tip = "Over 50% is a red flag. Focus on reducing debt ASAP." %}
{% endif %}
<div style="display:flex;align-items:center;gap:1.5rem;background:#fff;border:1px solid var(--border);border-radius:var(--radius);padding:1rem 1.25rem;margin-bottom:1rem;flex-wrap:wrap;">
<div>
<div style="font-size:0.75rem;text-transform:uppercase;letter-spacing:0.06em;color:var(--text-light);margin-bottom:0.2rem;">Debt-to-Income Ratio</div>
<div style="font-size:1.8rem;font-weight:700;color:{{ dti_color }};">{{ dti }}%</div>
</div>
<div style="flex:1;min-width:180px;">
<div style="display:flex;justify-content:space-between;font-size:0.78rem;margin-bottom:0.3rem;">
<span style="color:{{ dti_color }};font-weight:600;">{{ dti_label }}</span>
<span class="muted">${{ "%.0f" | format(grand.minimum) }} min / ${{ "%.0f" | format(monthly_income) }} income</span>
</div>
<div style="background:var(--border);border-radius:99px;height:8px;overflow:hidden;">
<div style="height:100%;width:{{ [dti, 100] | min }}%;background:{{ dti_color }};border-radius:99px;transition:width 0.3s;"></div>
</div>
<div style="font-size:0.75rem;color:var(--text-light);margin-top:0.3rem;">{{ dti_tip }}</div>
</div>
</div>
{% else %}
<div style="background:#fff;border:1px solid var(--border);border-radius:var(--radius);padding:0.75rem 1.25rem;margin-bottom:1rem;font-size:0.85rem;color:var(--text-light);">
DTI ratio unavailable — <a href="{{ url_for('income_view') }}">log this month's income</a> to see your debt-to-income ratio.
</div>
{% endif %}
<!-- Search -->
<div style="margin-bottom:1rem;">
<input type="text" id="debt-search" placeholder="Search debts…" class="budget-input" style="width:240px;" oninput="filterDebts(this.value)">
</div>
<!-- By Category -->
{% for cat in debt_categories %}
{% set debts = by_category[cat] %}
{% set cat_totals = totals[cat] %}
{% if debts %}
<section class="section">
<div class="debt-cat-header" onclick="toggleDebtSection('debt-section-{{ loop.index }}')" style="cursor:pointer;user-select:none;">
<div style="display:flex;align-items:center;gap:0.5rem;">
<span class="debt-collapse-arrow" id="arrow-debt-section-{{ loop.index }}"></span>
<h2 style="margin:0;">{{ cat }}</h2>
</div>
<div class="debt-cat-totals">
<span>Balance: <strong>${{ cat_totals.balance | money }}</strong></span>
<span>Minimums: <strong>${{ cat_totals.minimum | money }}</strong></span>
<span>Paid: <strong>${{ cat_totals.paid | money }}</strong></span>
<span class="accent">Extra: <strong>${{ cat_totals.extra | money }}</strong></span>
</div>
</div>
<div class="debt-cards" id="debt-section-{{ loop.index }}">
{% for d in debts %}
<div class="debt-card">
<div class="debt-card-top">
<div>
<div class="debt-card-name">{{ d.name }}</div>
{% if d.notes %}<div class="debt-card-notes">{{ d.notes }}</div>{% endif %}
</div>
<div class="debt-card-badges">
<span class="owner-tag" style="background: {{ owner_colors.get(d.owner, '#ddd') }};">{{ d.owner }}</span>
{% if d.apr %}<span class="apr-tag">{{ d.apr }}% APR</span>{% endif %}
{% if d.get('is_bnpl') %}
<button class="edit-link" style="background:none;border:none;cursor:pointer;font-size:0.78rem;"
onclick="openEditBNPL('{{ d.id }}','{{ d.name }}','{{ d.merchant or '' }}','{{ d.bnpl_provider or 'Affirm' }}','{{ d.owner }}','{{ d.original_amount or 0 }}','{{ d.payment_amount or 0 }}','{{ d.total_payments or 0 }}','{{ d.payments_made or 0 }}','{{ d.frequency or 'biweekly' }}','{{ d.next_payment_date or '' }}','{{ d.notes or '' }}')">
✏️ Edit
</button>
{% else %}
<button class="edit-link" style="background:none;border:none;cursor:pointer;font-size:0.78rem;"
onclick="openEditDebt('{{ d.id }}','{{ d.name }}','{{ d.owner }}','{{ d.apr or 0 }}','{{ d.minimum or 0 }}','{{ d.current_balance or 0 }}','{{ d.credit_limit or 0 }}','{{ d.notes or '' }}')">
✏️ Edit
</button>
{% endif %}
</div>
</div>
<div class="debt-card-balance">
<span class="debt-balance-label">Balance</span>
<span class="debt-balance-value" id="balance-{{ d.id }}">${{ d.ending | money }}</span>
</div>
{% if d.credit_limit %}
{% set available = d.credit_limit - d.ending %}
{% set util_pct = [(d.ending / d.credit_limit * 100), 100] | min | int %}
<div style="margin-bottom:0.75rem;">
<div style="display:flex;justify-content:space-between;font-size:0.8rem;margin-bottom:0.3rem;">
<span style="color:var(--text-light);">Available credit</span>
<span style="font-weight:600;color:{% if available < 0 %}#c07070{% elif util_pct > 80 %}#c07070{% elif util_pct > 50 %}#b8943f{% else %}#5A9E68{% endif %};">
${{ available | money }} of ${{ d.credit_limit | money }}
</span>
</div>
<div style="background:#EDE8E2;border-radius:4px;height:6px;">
<div style="background:{% if util_pct > 80 %}#c07070{% elif util_pct > 50 %}#d4a843{% else %}#5A9E68{% endif %};border-radius:4px;height:6px;width:{{ util_pct }}%;"></div>
</div>
<div style="font-size:0.73rem;color:var(--text-light);margin-top:0.2rem;">{{ util_pct }}% utilized</div>
</div>
{% endif %}
{% if d.payoff_str and not d.get('is_bnpl') %}
<div style="margin-bottom:0.75rem;">
<div style="display:flex;justify-content:space-between;font-size:0.8rem;margin-bottom:0.3rem;">
<span style="color:var(--text-light);">Payoff projection</span>
<span style="font-weight:600;color:#5A9E68;">{{ d.payoff_str }}</span>
</div>
{% if d.months_left and d.months_left <= 36 %}
{% set payoff_pct = [100 - (d.months_left / 36 * 100), 5] | max | int %}
<div style="background:#EDE8E2;border-radius:4px;height:6px;">
<div style="background:#5A9E68;border-radius:4px;height:6px;width:{{ payoff_pct }}%;"></div>
</div>
{% endif %}
<div style="font-size:0.73rem;color:var(--text-light);margin-top:0.2rem;">
{{ d.months_left }} month{{ 's' if d.months_left != 1 }} at minimum payments
{% if d.apr %} · {{ d.apr }}% APR{% endif %}
</div>
</div>
{% endif %}
<!-- Statement pending toggle -->
{% if not d.get('is_bnpl') %}
<div style="display:flex;align-items:center;gap:0.5rem;margin-bottom:0.75rem;">
<button class="toggle-btn {% if d.get('statement_pending') %}toggle-on{% endif %}"
onclick="togglePending('{{ d.id }}', this)"
style="font-size:0.75rem;padding:0.2rem 0.6rem;">
{{ '⏳ Statement Pending' if d.get('statement_pending') else 'Statement closed' }}
</button>
{% if d.get('statement_pending') %}
<span style="font-size:0.75rem;color:var(--text-light);">numbers may not reflect current statement</span>
{% endif %}
</div>
{% endif %}
{% if d.get('is_bnpl') %}
{% set pct = ((d.payments_made / d.total_payments) * 100) | int if d.total_payments else 0 %}
{% set remaining_payments = d.total_payments - d.payments_made %}
{% set payoff_str, _ = bnpl_payoff_date(d.payments_made, d.total_payments, d.frequency_days) %}
<div class="bnpl-progress-wrap">
<div class="bnpl-progress-bar">
<div class="bnpl-progress-fill" style="width: {{ pct }}%;"></div>
</div>
<div class="bnpl-progress-label">{{ d.payments_made }} of {{ d.total_payments }} payments made</div>
</div>
<table class="debt-month-table">
<tr><td>Payment Amount</td><td class="amount">${{ d.payment_amount | money }}</td></tr>
<tr><td>Payments Remaining</td><td class="amount">{{ remaining_payments }}</td></tr>
<tr><td>Still Owed</td><td class="amount">${{ d.current_balance | money }}</td></tr>
{% if payoff_str %}<tr><td>Payoff Date</td><td class="amount accent">{{ payoff_str }}</td></tr>{% endif %}
</table>
<form method="POST" action="{{ url_for('bnpl_payment', debt_id=d.id) }}" style="margin-bottom:0.5rem;">
<button type="submit" class="btn btn-accept" onclick="return confirm('Mark one payment as made?')">
✓ Mark Payment Made
</button>
</form>
{% else %}
<table class="debt-month-table">
<tr>
<td>Starting Balance</td>
<td><input type="number" step="0.01" class="budget-input"
value="{{ d.starting }}"
onchange="logDebt('{{ d.id }}', {{ year }}, {{ month }}, 'starting_balance', this.value, '{{ d.id }}')"
placeholder="0.00"></td>
</tr>
<tr>
<td>Interest Charged</td>
<td><input type="number" step="0.01" class="budget-input"
value="{{ d.interest }}"
onchange="logDebt('{{ d.id }}', {{ year }}, {{ month }}, 'interest', this.value, '{{ d.id }}')"
placeholder="auto-calculated"></td>
</tr>
<tr>
<td>New Charges</td>
<td><input type="number" step="0.01" class="budget-input"
value="{{ d.new_charges or '' }}"
onchange="logDebt('{{ d.id }}', {{ year }}, {{ month }}, 'new_charges', this.value, '{{ d.id }}')"
placeholder="0.00"></td>
</tr>
<tr>
<td>Minimum Due</td>
<td class="amount">${{ d.minimum | money }}</td>
</tr>
<tr>
<td>Payment Made</td>
<td><input type="number" step="0.01" class="budget-input"
value="{{ d.payment or '' }}"
onchange="logDebt('{{ d.id }}', {{ year }}, {{ month }}, 'payment', this.value, '{{ d.id }}')"
placeholder="0.00"></td>
</tr>
<tr class="debt-extra-row">
{% set underpaid = d.payment > 0 and d.payment < d.minimum and not d.get('statement_pending') %}
<td>{% if underpaid %}⚠️ Underpaid Minimum{% else %}Extra Paid Above Minimum{% endif %}</td>
<td class="amount {% if underpaid %}{% else %}accent{% endif %}" id="extra-{{ d.id }}"
{% if underpaid %}style="color:#c07070;"{% endif %}>
{% if underpaid %}
-${{ d.minimum - d.payment | money }} short
{% elif d.get('statement_pending') %}
<span class="muted"></span>
{% else %}
${{ d.extra | money }}
{% endif %}
</td>
</tr>
</table>
{% endif %}
{% if not d.get('is_bnpl') %}
<button class="btn btn-ghost" style="font-size:0.78rem;margin-bottom:0.5rem;width:100%;"
onclick="openStatementModal('{{ d.id }}', '{{ d.name }}')">📋 Paste Statement</button>
{% endif %}
<form method="POST" action="{{ url_for('debt_delete', debt_id=d.id) }}"
onsubmit="return confirm('Remove {{ d.name }} from debt tracker?');">
<button type="submit" class="debt-delete-btn">Remove</button>
</form>
</div>
{% endfor %}
</div>
</section>
{% endif %}
{% endfor %}
{% if not (by_category.values() | map('list') | sum(start=[])) %}
<div class="empty-state">
<p>No debts added yet. Click "+ Add a Debt" above to get started.</p>
</div>
{% endif %}
<script>
function toggleDebtSection(id) {
const el = document.getElementById(id);
const arrow = document.getElementById('arrow-' + id);
if (!el) return;
const collapsed = el.style.display === 'none';
el.style.display = collapsed ? '' : 'none';
if (arrow) arrow.textContent = collapsed ? '▾' : '▸';
}
function togglePending(debtId, btn) {
fetch(`/debts/toggle-pending/${debtId}`, {method: 'POST'})
.then(r => r.json())
.then(data => {
btn.textContent = data.pending ? '⏳ Statement Pending' : 'Statement closed';
btn.classList.toggle('toggle-on', data.pending);
location.reload();
});
}
function filterDebts(q) {
q = q.toLowerCase();
document.querySelectorAll('.debt-card').forEach(card => {
card.style.display = card.textContent.toLowerCase().includes(q) ? '' : 'none';
});
}
function logDebt(debtId, year, month, field, value, cardId) {
const body = new URLSearchParams({debt_id: debtId, year, month, field, value});
fetch('/debts/log', {method: 'POST', body})
.then(r => r.json())
.then(data => {
if (data.ending !== undefined) {
const bal = document.getElementById('balance-' + cardId);
if (bal) bal.textContent = '$' + data.ending.toFixed(2);
}
if (data.extra !== undefined) {
const ext = document.getElementById('extra-' + cardId);
if (ext) ext.textContent = '$' + data.extra.toFixed(2);
}
});
}
</script>
<!-- Edit Debt Modal -->
<div id="edit-debt-modal" style="display:none;position:fixed;inset:0;background:rgba(0,0,0,0.3);z-index:200;align-items:center;justify-content:center;">
<div style="background:#fff;border-radius:12px;padding:1.5rem;width:90%;max-width:480px;box-shadow:0 8px 32px rgba(0,0,0,0.18);">
<h3 style="margin:0 0 1rem;" id="edit-debt-title">Edit Debt</h3>
<form method="POST" id="edit-debt-form" action="">
<div style="display:grid;grid-template-columns:1fr 1fr;gap:0.75rem;">
<div class="form-group">
<label style="font-size:0.85rem;">Card / Debt Name</label>
<input type="text" name="name" id="edit-name" class="budget-input" style="width:100%;" required>
</div>
<div class="form-group">
<label style="font-size:0.85rem;">Owner</label>
<select name="owner" id="edit-owner" class="budget-input" style="width:100%;">
<option value="Bonna">Bonna</option>
<option value="Tony">Tony</option>
<option value="Both">Both</option>
</select>
</div>
<div class="form-group">
<label style="font-size:0.85rem;">Current Balance</label>
<input type="number" name="current_balance" id="edit-balance" step="0.01" min="0" class="budget-input" style="width:100%;">
</div>
<div class="form-group">
<label style="font-size:0.85rem;">APR %</label>
<input type="number" name="apr" id="edit-apr" step="0.01" min="0" class="budget-input" style="width:100%;">
</div>
<div class="form-group">
<label style="font-size:0.85rem;">Minimum Payment</label>
<input type="number" name="minimum" id="edit-minimum" step="0.01" min="0" class="budget-input" style="width:100%;">
</div>
<div class="form-group">
<label style="font-size:0.85rem;">Credit Limit</label>
<input type="number" name="credit_limit" id="edit-credit-limit" step="0.01" min="0" class="budget-input" style="width:100%;">
</div>
</div>
<div class="form-group" style="margin-top:0.5rem;">
<label style="font-size:0.85rem;">Notes</label>
<input type="text" name="notes" id="edit-notes" class="budget-input" style="width:100%;" placeholder="optional">
</div>
<div style="display:flex;gap:0.75rem;justify-content:flex-end;margin-top:1rem;">
<button type="button" class="btn btn-ghost" onclick="closeEditDebt()">Cancel</button>
<button type="submit" class="btn btn-primary">Save Changes</button>
</div>
</form>
</div>
</div>
<!-- Edit BNPL Modal -->
<div id="edit-bnpl-modal" style="display:none;position:fixed;inset:0;background:rgba(0,0,0,0.3);z-index:200;align-items:center;justify-content:center;">
<div style="background:#fff;border-radius:12px;padding:1.5rem;width:90%;max-width:520px;box-shadow:0 8px 32px rgba(0,0,0,0.18);max-height:90vh;overflow-y:auto;">
<h3 style="margin:0 0 1rem;" id="edit-bnpl-title">Edit BNPL Plan</h3>
<form method="POST" id="edit-bnpl-form" action="">
<div style="display:grid;grid-template-columns:1fr 1fr;gap:0.75rem;">
<div class="form-group">
<label style="font-size:0.85rem;">What is it for?</label>
<input type="text" name="description" id="ebnpl-description" class="budget-input" style="width:100%;" required>
</div>
<div class="form-group">
<label style="font-size:0.85rem;">Merchant / Store</label>
<input type="text" name="merchant" id="ebnpl-merchant" class="budget-input" style="width:100%;">
</div>
<div class="form-group">
<label style="font-size:0.85rem;">Provider</label>
<select name="bnpl_provider" id="ebnpl-provider" class="budget-input" style="width:100%;">
<option>Affirm</option>
<option>Klarna</option>
<option>Afterpay</option>
<option>Zip</option>
<option>Other</option>
</select>
</div>
<div class="form-group">
<label style="font-size:0.85rem;">Owner</label>
<select name="owner" id="ebnpl-owner" class="budget-input" style="width:100%;">
<option value="Bonna">Bonna</option>
<option value="Tony">Tony</option>
<option value="Both">Both</option>
</select>
</div>
<div class="form-group">
<label style="font-size:0.85rem;">Original Purchase Amount</label>
<input type="number" step="0.01" name="original_amount" id="ebnpl-original" class="budget-input" style="width:100%;" placeholder="0.00">
</div>
<div class="form-group">
<label style="font-size:0.85rem;">Payment Amount</label>
<input type="number" step="0.01" name="payment_amount" id="ebnpl-payment" class="budget-input" style="width:100%;" placeholder="0.00">
</div>
<div class="form-group">
<label style="font-size:0.85rem;">Total Payments</label>
<input type="number" min="1" name="total_payments" id="ebnpl-total" class="budget-input" style="width:100%;">
</div>
<div class="form-group">
<label style="font-size:0.85rem;">Payments Already Made</label>
<input type="number" min="0" name="payments_made" id="ebnpl-made" class="budget-input" style="width:100%;">
</div>
<div class="form-group">
<label style="font-size:0.85rem;">Frequency</label>
<select name="frequency" id="ebnpl-frequency" class="budget-input" style="width:100%;">
<option value="biweekly">Every 2 weeks</option>
<option value="monthly">Monthly</option>
<option value="4-payment">4 payments (every 2 weeks)</option>
</select>
</div>
<div class="form-group">
<label style="font-size:0.85rem;">Next Payment Date</label>
<input type="date" name="next_payment_date" id="ebnpl-next-date" class="budget-input" style="width:100%;">
</div>
</div>
<div class="form-group" style="margin-top:0.5rem;">
<label style="font-size:0.85rem;">Notes</label>
<input type="text" name="notes" id="ebnpl-notes" class="budget-input" style="width:100%;" placeholder="optional">
</div>
<p class="muted" style="font-size:0.78rem;margin-top:0.75rem;">Saving will delete any unpaid calendar entries for this plan and regenerate them based on the updated dates.</p>
<div style="display:flex;gap:0.75rem;justify-content:flex-end;margin-top:1rem;">
<button type="button" class="btn btn-ghost" onclick="closeEditBNPL()">Cancel</button>
<button type="submit" class="btn btn-primary">Save Changes</button>
</div>
</form>
</div>
</div>
<!-- Paste Statement Modal -->
<div id="statement-modal" style="display:none;position:fixed;inset:0;background:rgba(0,0,0,0.3);z-index:200;align-items:center;justify-content:center;">
<div style="background:#fff;border-radius:12px;padding:1.5rem;width:90%;max-width:540px;box-shadow:0 8px 32px rgba(0,0,0,0.18);">
<h3 style="margin:0 0 0.25rem;" id="statement-modal-title">Paste Statement</h3>
<p class="muted" style="font-size:0.82rem;margin-bottom:1rem;">Copy all the text from your statement and paste it below. The app will extract the numbers automatically.</p>
<textarea id="statement-text" style="width:100%;height:180px;border:1px solid var(--border);border-radius:8px;padding:0.75rem;font-size:0.82rem;resize:vertical;" placeholder="Paste statement text here…"></textarea>
<div id="statement-result" style="margin:0.75rem 0;font-size:0.85rem;"></div>
<div style="display:flex;gap:0.75rem;justify-content:flex-end;margin-top:0.75rem;">
<button class="btn btn-ghost" onclick="closeStatementModal()">Cancel</button>
<button class="btn btn-primary" onclick="parseStatement()">Import</button>
</div>
</div>
</div>
<script>
let activeDebtId = null;
function addCardFromStatement() {
const name = document.getElementById('paste-card-name').value.trim();
const owner = document.getElementById('paste-card-owner').value;
const text = document.getElementById('paste-new-statement').value.trim();
const resultEl = document.getElementById('paste-new-result');
if (!name) { resultEl.style.color='#c07070'; resultEl.textContent='Please enter a card name.'; return; }
if (!text) { resultEl.style.color='#c07070'; resultEl.textContent='Please paste your statement text.'; return; }
resultEl.style.color='inherit';
resultEl.textContent='Creating card and importing…';
fetch('/debts/add-from-statement', {
method: 'POST',
headers: {'Content-Type': 'application/x-www-form-urlencoded'},
body: new URLSearchParams({name, owner, text, year: {{ year }}, month: {{ month }}})
})
.then(r => r.json())
.then(data => {
if (data.error) { resultEl.style.color='#c07070'; resultEl.textContent=data.error; return; }
resultEl.style.color='#5A9E68';
resultEl.textContent='✓ Card added and statement imported!';
setTimeout(() => location.reload(), 1200);
});
}
function openStatementModal(debtId, debtName) {
activeDebtId = debtId;
document.getElementById('statement-modal-title').textContent = 'Paste Statement — ' + debtName;
document.getElementById('statement-text').value = '';
document.getElementById('statement-result').textContent = '';
document.getElementById('statement-modal').style.display = 'flex';
}
function closeStatementModal() {
document.getElementById('statement-modal').style.display = 'none';
activeDebtId = null;
}
function parseStatement() {
const text = document.getElementById('statement-text').value;
if (!text.trim()) return;
const resultEl = document.getElementById('statement-result');
resultEl.textContent = 'Parsing…';
fetch('/debts/parse-statement', {
method: 'POST',
headers: {'Content-Type': 'application/x-www-form-urlencoded'},
body: new URLSearchParams({text, debt_id: activeDebtId, year: {{ year }}, month: {{ month }}})
})
.then(r => r.json())
.then(data => {
if (data.error) {
resultEl.style.color = '#c07070';
resultEl.textContent = 'Could not parse: ' + data.error;
return;
}
resultEl.style.color = '#5A9E68';
resultEl.innerHTML = `
✓ Imported:<br>
Previous balance: <strong>$${data.previous_balance}</strong> &nbsp;
Purchases: <strong>$${data.purchases}</strong> &nbsp;
Interest: <strong>$${data.interest}</strong> &nbsp;
Payment: <strong>$${data.payment}</strong> &nbsp;
Minimum due: <strong>$${data.minimum}</strong> &nbsp;
New balance: <strong>$${data.new_balance}</strong>
${data.credit_limit ? '&nbsp; Credit limit: <strong>$' + data.credit_limit + '</strong>' : ''}
${data.available_credit ? '&nbsp; Available: <strong>$' + data.available_credit + '</strong>' : ''}
`;
// Reload to show updated values
setTimeout(() => { closeStatementModal(); location.reload(); }, 1800);
});
}
document.getElementById('statement-modal').addEventListener('click', function(e) {
if (e.target === this) closeStatementModal();
});
function openEditDebt(id, name, owner, apr, minimum, balance, creditLimit, notes) {
document.getElementById('edit-debt-title').textContent = 'Edit — ' + name;
document.getElementById('edit-debt-form').action = '/debts/edit/' + id;
document.getElementById('edit-name').value = name;
document.getElementById('edit-owner').value = owner;
document.getElementById('edit-apr').value = apr != '0' ? apr : '';
document.getElementById('edit-minimum').value = minimum != '0' ? minimum : '';
document.getElementById('edit-balance').value = balance != '0' ? balance : '';
document.getElementById('edit-credit-limit').value = creditLimit != '0' ? creditLimit : '';
document.getElementById('edit-notes').value = notes;
document.getElementById('edit-debt-modal').style.display = 'flex';
}
function closeEditDebt() {
document.getElementById('edit-debt-modal').style.display = 'none';
}
document.getElementById('edit-debt-modal').addEventListener('click', function(e) {
if (e.target === this) closeEditDebt();
});
function openEditBNPL(id, description, merchant, provider, owner, original, payment, total, made, frequency, nextDate, notes) {
document.getElementById('edit-bnpl-title').textContent = 'Edit — ' + description;
document.getElementById('edit-bnpl-form').action = '/debts/bnpl-edit/' + id;
document.getElementById('ebnpl-description').value = description;
document.getElementById('ebnpl-merchant').value = merchant;
document.getElementById('ebnpl-provider').value = provider;
document.getElementById('ebnpl-owner').value = owner;
document.getElementById('ebnpl-original').value = original != '0' ? original : '';
document.getElementById('ebnpl-payment').value = payment != '0' ? payment : '';
document.getElementById('ebnpl-total').value = total;
document.getElementById('ebnpl-made').value = made;
document.getElementById('ebnpl-frequency').value = frequency;
document.getElementById('ebnpl-next-date').value = nextDate;
document.getElementById('ebnpl-notes').value = notes;
document.getElementById('edit-bnpl-modal').style.display = 'flex';
}
function closeEditBNPL() {
document.getElementById('edit-bnpl-modal').style.display = 'none';
}
document.getElementById('edit-bnpl-modal').addEventListener('click', function(e) {
if (e.target === this) closeEditBNPL();
});
</script>
{% endblock %}