fix: Render repo mix before populating counts #49
|
|
@ -106,7 +106,16 @@ textarea { resize: vertical; min-height: 120px; }
|
|||
</details>
|
||||
<details class="panel" data-panel-key="widgets" open>
|
||||
<summary><h2>Live Widgets</h2></summary>
|
||||
<div id="widgets" class="stack"></div>
|
||||
<div id="widgets" class="stack">
|
||||
<div class="widget">
|
||||
<h3>Live clock</h3>
|
||||
<div class="small" id="widget-clock">—</div>
|
||||
</div>
|
||||
<div class="widget">
|
||||
<h3>Repo mix</h3>
|
||||
<div class="small" id="repo-mix">Loading…</div>
|
||||
</div>
|
||||
</div>
|
||||
</details>
|
||||
</section>
|
||||
|
||||
|
|
@ -358,33 +367,20 @@ textarea { resize: vertical; min-height: 120px; }
|
|||
tickWidgets();
|
||||
|
||||
/* Widgets */
|
||||
const widgetOrder = ['countdown','repoAtAGlance'];
|
||||
const widgetRegistry = {
|
||||
countdown: () => ({ title: 'Live clock', html: '<div class="small" id="widget-clock">—</div>' }),
|
||||
repoAtAGlance: async () => {
|
||||
const el = qs('#repo-mix');
|
||||
if (!el) return { title:'Repo mix', html:'<div class="small">Unavailable</div>' };
|
||||
try {
|
||||
const res = await fetch('/api/v1/context', { headers: { Accept: 'application/json' } });
|
||||
const data = await res.json();
|
||||
const repos = data.repos || [];
|
||||
const issues = (data.issues||[]).filter(i=>i.state==='open');
|
||||
const prs = data.pull_requests || [];
|
||||
el.innerHTML = '<div class="kv"><div class="label">Repos</div><div class="value">' + repos.length + '</div><div class="label">Open issues</div><div class="value">' + issues.length + '</div><div class="label">Open PRs</div><div class="value">' + prs.length + '</div></div>';
|
||||
} catch (e) { el.innerHTML = '<div class="muted">Widget unavailable.</div>'; }
|
||||
return { title:'Repo mix', html:'<div class="small" id="repo-mix">Loading…</div>' };
|
||||
},
|
||||
};
|
||||
const widgetsEl = qs('#widgets');
|
||||
function paintWidgets() {
|
||||
widgetsEl.innerHTML = '';
|
||||
widgetOrder.forEach(async k => {
|
||||
const w = await widgetRegistry[k]();
|
||||
const node = document.createElement('div'); node.className='widget'; node.innerHTML = '<h3>' + escapeHtml(w.title) + '</h3>' + w.html;
|
||||
widgetsEl.appendChild(node);
|
||||
});
|
||||
async function updateRepoMix() {
|
||||
const el = qs('#repo-mix');
|
||||
try {
|
||||
const res = await fetch('/api/v1/context', { headers: { Accept: 'application/json' } });
|
||||
const data = await res.json();
|
||||
const repos = data.repos || [];
|
||||
const issues = (data.issues||[]).filter(i=>i.state==='open');
|
||||
const prs = data.pull_requests || [];
|
||||
el.innerHTML = '<div class="kv"><div class="label">Repos</div><div class="value">' + repos.length + '</div><div class="label">Open issues</div><div class="value">' + issues.length + '</div><div class="label">Open PRs</div><div class="value">' + prs.length + '</div></div>';
|
||||
} catch (e) {
|
||||
el.innerHTML = '<div class="muted">Widget unavailable.</div>';
|
||||
}
|
||||
}
|
||||
paintWidgets();
|
||||
updateRepoMix();
|
||||
|
||||
function widgetTick() { const el=qs('#widget-clock'); if(el) el.textContent = fmt(new Date()); }
|
||||
setInterval(widgetTick, 1000);
|
||||
|
|
|
|||
11
tests/test_widgets.py
Normal file
11
tests/test_widgets.py
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
import pytest
|
||||
|
||||
from src.views import dashboard
|
||||
|
||||
|
||||
@pytest.mark.anyio
|
||||
async def test_dashboard_renders_repo_mix_placeholder_before_javascript_runs():
|
||||
html = await dashboard()
|
||||
server_rendered_html = html.split("<script>", 1)[0]
|
||||
|
||||
assert '<div class="small" id="repo-mix">Loading…</div>' in server_rendered_html
|
||||
Loading…
Reference in New Issue
Block a user