06/05 Optimize app
This commit is contained in:
@@ -7,20 +7,42 @@
|
||||
<form method="POST" action="{{ url_for('schwab.sync_confirm') }}" id="importForm">
|
||||
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}">
|
||||
|
||||
<div class="pcard mb-3 d-flex justify-content-between align-items-center flex-wrap gap-2">
|
||||
<div>
|
||||
<div style="font-size:14px;font-weight:600;">{{ sa.account_name }} ({{ sa.account_number_display }})</div>
|
||||
<div style="font-size:12px;color:var(--muted);">
|
||||
Mapped to: <strong>{{ sa.pfm_account.name }}</strong> ·
|
||||
<span id="selectedCount">{{ count }}</span> of {{ count }} selected
|
||||
{% if sa.last_sync_date %}· Last sync: {{ sa.last_sync_date.strftime('%b %d, %Y') }}{% endif %}
|
||||
<div class="pcard mb-3">
|
||||
<div class="d-flex justify-content-between align-items-start flex-wrap gap-2">
|
||||
<div>
|
||||
<div style="font-size:14px;font-weight:600;">{{ sa.account_name }} ({{ sa.account_number_display }})</div>
|
||||
<div style="font-size:12px;color:var(--muted);">
|
||||
Mapped to: <strong>{{ sa.pfm_account.name }}</strong> ·
|
||||
<span id="selectedCount">{{ count }}</span> of {{ count }} selected
|
||||
{% if sa.last_sync_date %}· Last sync: {{ sa.last_sync_date.strftime('%b %d, %Y') }}{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
<div class="d-flex gap-2">
|
||||
<a href="{{ url_for('schwab.index') }}" class="btn btn-sm btn-outline-secondary" style="font-size:12px;">Cancel</a>
|
||||
<button type="submit" class="btn btn-sm btn-success" style="font-size:12px;" id="importBtn">
|
||||
<i class="bi bi-check-lg me-1"></i>Import <span id="importBtnCount">{{ count }}</span> Transaction(s)
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="d-flex gap-2">
|
||||
<a href="{{ url_for('schwab.index') }}" class="btn btn-sm btn-outline-secondary" style="font-size:12px;">Cancel</a>
|
||||
<button type="submit" class="btn btn-sm btn-success" style="font-size:12px;" id="importBtn">
|
||||
<i class="bi bi-check-lg me-1"></i>Import <span id="importBtnCount">{{ count }}</span> Transaction(s)
|
||||
|
||||
<!-- Bulk actions bar -->
|
||||
<div class="mt-3 pt-3 d-flex align-items-center flex-wrap gap-2" style="border-top:1px solid var(--border);">
|
||||
<span style="font-size:12px;color:var(--muted);font-weight:500;">Bulk set selected:</span>
|
||||
<button type="button" class="btn btn-sm btn-outline-danger" id="bulkExpense" style="font-size:12px;">
|
||||
<i class="bi bi-dash-circle me-1"></i>Expense
|
||||
</button>
|
||||
<button type="button" class="btn btn-sm btn-outline-success" id="bulkIncome" style="font-size:12px;">
|
||||
<i class="bi bi-plus-circle me-1"></i>Income
|
||||
</button>
|
||||
<div class="d-flex align-items-center gap-1 ms-1">
|
||||
<select id="bulkCategory" class="form-select form-select-sm" style="min-width:150px;font-size:12px;">
|
||||
<option value="">— Category —</option>
|
||||
{% for c in categories %}
|
||||
<option value="{{ c.id }}">{{ c.name }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
<button type="button" class="btn btn-sm btn-outline-secondary" id="bulkApplyCat" style="font-size:12px;">Apply</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -35,7 +57,7 @@
|
||||
<th>Description</th>
|
||||
<th class="d-mob-none">Schwab Type</th>
|
||||
<th style="width:160px;">PFM Type</th>
|
||||
<th class="d-mob-none">Category</th>
|
||||
<th>Category</th>
|
||||
<th class="text-end" style="padding-right:20px;">Amount</th>
|
||||
</tr>
|
||||
</thead>
|
||||
@@ -61,12 +83,14 @@
|
||||
<option value="income" {% if txn.transaction_type == 'income' %}selected{% endif %}>Income</option>
|
||||
</select>
|
||||
</td>
|
||||
<td class="d-mob-none" style="font-size:12px;color:var(--muted);">
|
||||
{% if txn.category_id and txn.category_id in cat_map %}
|
||||
{{ cat_map[txn.category_id] }}
|
||||
{% else %}
|
||||
<span style="color:#f59e0b;">Uncategorised</span>
|
||||
{% endif %}
|
||||
<td>
|
||||
<select name="category_{{ txn.schwab_id }}" class="cat-select form-select form-select-sm"
|
||||
style="font-size:12px;min-width:140px;">
|
||||
<option value="">— Uncategorised —</option>
|
||||
{% for c in categories %}
|
||||
<option value="{{ c.id }}" {% if txn.category_id == c.id %}selected{% endif %}>{{ c.name }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</td>
|
||||
<td class="text-end mono amount-cell
|
||||
{% if txn.transaction_type == 'income' %}text-income{% else %}text-expense{% endif %}"
|
||||
@@ -121,19 +145,48 @@
|
||||
});
|
||||
checks.forEach(cb => cb.addEventListener('change', updateCount));
|
||||
|
||||
function applyTypeStyle(sel) {
|
||||
const isIncome = sel.value === 'income';
|
||||
sel.style.borderColor = isIncome ? '#10b981' : '#ef4444';
|
||||
sel.style.color = isIncome ? '#10b981' : '#ef4444';
|
||||
const row = sel.closest('.preview-row');
|
||||
const cell = row.querySelector('.amount-cell');
|
||||
const sign = row.querySelector('.sign');
|
||||
cell.classList.toggle('text-income', isIncome);
|
||||
cell.classList.toggle('text-expense', !isIncome);
|
||||
sign.textContent = isIncome ? '+' : '-';
|
||||
}
|
||||
|
||||
document.querySelectorAll('.type-select').forEach(sel => {
|
||||
sel.addEventListener('change', function () {
|
||||
const isIncome = this.value === 'income';
|
||||
this.style.borderColor = isIncome ? '#10b981' : '#ef4444';
|
||||
this.style.color = isIncome ? '#10b981' : '#ef4444';
|
||||
const row = this.closest('.preview-row');
|
||||
const cell = row.querySelector('.amount-cell');
|
||||
const sign = row.querySelector('.sign');
|
||||
cell.classList.toggle('text-income', isIncome);
|
||||
cell.classList.toggle('text-expense', !isIncome);
|
||||
sign.textContent = isIncome ? '+' : '-';
|
||||
sel.addEventListener('change', function () { applyTypeStyle(this); });
|
||||
});
|
||||
|
||||
// Bulk: set type
|
||||
document.getElementById('bulkExpense').addEventListener('click', () => {
|
||||
document.querySelectorAll('.row-check:checked').forEach(cb => {
|
||||
const sel = cb.closest('.preview-row').querySelector('.type-select');
|
||||
sel.value = 'expense';
|
||||
applyTypeStyle(sel);
|
||||
});
|
||||
});
|
||||
document.getElementById('bulkIncome').addEventListener('click', () => {
|
||||
document.querySelectorAll('.row-check:checked').forEach(cb => {
|
||||
const sel = cb.closest('.preview-row').querySelector('.type-select');
|
||||
sel.value = 'income';
|
||||
applyTypeStyle(sel);
|
||||
});
|
||||
});
|
||||
|
||||
// Bulk: set category
|
||||
document.getElementById('bulkApplyCat').addEventListener('click', () => {
|
||||
const catId = document.getElementById('bulkCategory').value;
|
||||
if (!catId) return;
|
||||
document.querySelectorAll('.row-check:checked').forEach(cb => {
|
||||
const sel = cb.closest('.preview-row').querySelector('.cat-select');
|
||||
if (sel) sel.value = catId;
|
||||
});
|
||||
document.getElementById('bulkCategory').value = '';
|
||||
});
|
||||
})();
|
||||
</script>
|
||||
{% endblock %}
|
||||
|
||||
Reference in New Issue
Block a user