06/05 Optimize app

This commit is contained in:
2026-06-05 15:23:23 -04:00
parent db44d6057b
commit 9c9aa694c4
9 changed files with 781 additions and 19 deletions
+98
View File
@@ -138,6 +138,39 @@
</div>
</div>
<!-- Financial Health Score -->
<div id="health-score-card" class="pcard mb-4" style="display:none;">
<div class="d-flex justify-content-between align-items-center mb-3 flex-wrap gap-2">
<span class="pcard-title mb-0">Financial Health Score</span>
<span id="hs-grade-badge" class="badge fw-bold" style="font-size:13px;padding:4px 12px;"></span>
</div>
<div class="row g-3 align-items-center">
<!-- Score ring -->
<div class="col-12 col-sm-auto d-flex justify-content-center">
<div style="position:relative;width:96px;height:96px;">
<svg viewBox="0 0 36 36" style="width:96px;height:96px;transform:rotate(-90deg);">
<circle cx="18" cy="18" r="15.9155" fill="none" stroke="var(--border)" stroke-width="3"/>
<circle id="hs-ring" cx="18" cy="18" r="15.9155" fill="none" stroke="#10b981" stroke-width="3"
stroke-dasharray="0 100" stroke-linecap="round"
style="transition:stroke-dasharray .8s ease, stroke .4s;"/>
</svg>
<div style="position:absolute;inset:0;display:flex;flex-direction:column;align-items:center;justify-content:center;">
<div id="hs-score" class="mono fw-bold" style="font-size:22px;line-height:1;"></div>
<div style="font-size:10px;color:var(--muted);">/ 100</div>
</div>
</div>
</div>
<!-- Component bars -->
<div class="col">
<div id="hs-components" class="d-flex flex-column gap-2"></div>
</div>
</div>
<!-- Tips -->
<div id="hs-tips" class="mt-3" style="display:none;">
<div class="d-flex flex-wrap gap-2" id="hs-tips-list"></div>
</div>
</div>
<!-- Charts Row -->
<div class="row g-3 mb-4">
<div class="col-12 col-xl-8">
@@ -555,6 +588,71 @@ function refreshFxRate() {
});
})();
// ── Financial health score ───────────────────────────────────────────────────
(function(){
var card = document.getElementById('health-score-card');
var ring = document.getElementById('hs-ring');
var scoreEl = document.getElementById('hs-score');
var gradeEl = document.getElementById('hs-grade-badge');
var compsEl = document.getElementById('hs-components');
var tipsWrap = document.getElementById('hs-tips');
var tipsList = document.getElementById('hs-tips-list');
if (!card) return;
const SYM = '{{ current_user.currency_symbol }}';
fetch('/api/health-score')
.then(r => r.json())
.then(data => {
if (data.error) return;
card.style.display = '';
// Score ring
var circ = 100;
ring.style.strokeDasharray = (data.score / 100 * circ) + ' ' + circ;
ring.style.stroke = data.color;
scoreEl.textContent = data.score;
scoreEl.style.color = data.color;
// Grade badge
gradeEl.textContent = 'Grade ' + data.grade;
gradeEl.style.background = data.color + '22';
gradeEl.style.color = data.color;
// Component bars
var icons = {
savings: 'bi-piggy-bank',
budgets: 'bi-pie-chart',
goals: 'bi-bullseye',
emergency_fund: 'bi-shield-check',
};
compsEl.innerHTML = data.components.map(c => `
<div>
<div class="d-flex justify-content-between align-items-center mb-1" style="font-size:12px;">
<span><i class="bi ${c.icon} me-1" style="color:var(--muted);"></i>${c.name}</span>
<span class="mono" style="color:var(--muted);">${c.points}/${c.max}</span>
</div>
<div class="progress" style="height:5px;border-radius:3px;">
<div class="progress-bar" role="progressbar"
style="width:${c.pct}%;background:${c.pct>=80?'#10b981':c.pct>=50?'#3b82f6':c.pct>=25?'#f59e0b':'#ef4444'};transition:width .6s ease;">
</div>
</div>
${c.label ? `<div style="font-size:10px;color:var(--muted);margin-top:1px;">${c.label}</div>` : ''}
</div>`).join('');
// Tips
if (data.tips && data.tips.length) {
tipsWrap.style.display = '';
tipsList.innerHTML = data.tips.map(t =>
`<span style="font-size:11px;background:#fef3c7;color:#92400e;border-radius:20px;padding:3px 10px;display:inline-block;">
<i class="bi bi-lightbulb me-1"></i>${t}
</span>`
).join('');
}
})
.catch(() => {});
})();
// ── Spending anomalies ───────────────────────────────────────────────────────
(function(){
const card = document.getElementById('anomaly-card');