Skip to content

Commit 203d543

Browse files
authored
Update index.html
1 parent 630073c commit 203d543

File tree

1 file changed

+66
-40
lines changed

1 file changed

+66
-40
lines changed

index.html

Lines changed: 66 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -1,35 +1,36 @@
11
<script>
2-
// ---- CONFIG ----
2+
// ===== Configuration =====
33
const ORG = 'Democracy-Lab';
4-
const ORG_PAGES_BASE = 'https://democracy-lab.github.io'; // base org pages host
4+
const ORG_PAGES_BASE = 'https://democracy-lab.github.io';
5+
const SHOW_ALL = true; // true = show all public repos immediately; false = only those with Pages
6+
const AUTO_REFRESH_MS = 120000; // 2 minutes; set to 0 to disable auto-refresh
57

6-
// Fetch all org repos (handles pagination if >100)
7-
async function fetchAllRepos(url = `https://api.github.com/orgs/${ORG}/repos?per_page=100&type=public`) {
8+
// Build the expected Pages URL for a repo (works for org project pages)
9+
function pagesUrl(repoName) {
10+
if (repoName.toLowerCase() === `${ORG.toLowerCase()}.github.io`) return `${ORG_PAGES_BASE}/`;
11+
return `${ORG_PAGES_BASE}/${encodeURIComponent(repoName)}/`;
12+
}
13+
14+
// Fetch all public repos with pagination; bust caches aggressively
15+
async function fetchAllRepos(url = `https://api.github.com/orgs/${ORG}/repos?per_page=100&type=public&ts=${Date.now()}`) {
816
const all = [];
917
while (url) {
10-
const res = await fetch(url);
18+
const res = await fetch(url, {
19+
cache: 'no-store',
20+
headers: { 'Accept': 'application/vnd.github+json' }
21+
});
1122
if (!res.ok) throw new Error(`GitHub API: ${res.status} ${res.statusText}`);
1223
const page = await res.json();
1324
all.push(...page);
14-
// parse Link header for pagination
25+
1526
const link = res.headers.get('Link');
1627
const next = link && link.split(',').find(s => s.includes('rel="next"'));
17-
url = next ? next.split(';')[0].trim().slice(1, -1) : null;
28+
url = next ? next.split(';')[0].trim().slice(1, -1) + `&ts=${Date.now()}` : null;
1829
}
1930
return all;
2031
}
2132

22-
// Construct org Pages URL for a repo
23-
function pagesUrl(repo) {
24-
if (repo.name.toLowerCase() === `${ORG.toLowerCase()}.github.io`) {
25-
return `${ORG_PAGES_BASE}/`;
26-
}
27-
return `${ORG_PAGES_BASE}/${encodeURIComponent(repo.name)}/`;
28-
}
29-
30-
function fmtDate(iso) {
31-
try { return new Date(iso).toLocaleString(); } catch { return iso; }
32-
}
33+
function fmtDate(iso) { try { return new Date(iso).toLocaleString(); } catch { return iso; } }
3334

3435
function render(list) {
3536
const grid = document.getElementById('grid');
@@ -39,29 +40,35 @@
3940
empty.style.display = 'none';
4041

4142
for (const r of list) {
42-
const site = pagesUrl(r);
43+
const live = !!r.has_pages || r.name.toLowerCase() === `${ORG.toLowerCase()}.github.io`;
44+
const site = pagesUrl(r.name);
4345

4446
const card = document.createElement('div');
4547
card.className = 'card';
48+
if (!live) card.style.opacity = 0.6;
49+
4650
card.innerHTML = `
4751
<h3>
48-
<a href="${site}" target="_blank" rel="noopener" style="color:#67b7ff;text-decoration:none;">
52+
<a href="${live ? site : r.html_url}" target="_blank" rel="noopener"
53+
style="color:#67b7ff;text-decoration:none;">
4954
${r.name}
5055
</a>
5156
</h3>
5257
<div class="meta">${r.description ? r.description : ''}</div>
5358
<div class="meta">Updated: ${fmtDate(r.updated_at)}</div>
5459
<div class="links">
55-
<a href="${site}" target="_blank" rel="noopener">View site</a>
60+
${live
61+
? `<a href="${site}" target="_blank" rel="noopener">View site</a>`
62+
: `<span class="meta">Not deployed yet</span>`}
5663
<a href="${r.html_url}" target="_blank" rel="noopener">Repo</a>
5764
</div>
5865
`;
5966

60-
// Make the whole card open the site when clicked (but not when clicking an inner link)
67+
// Make the whole card open (site if live, else repo) unless a link was clicked
6168
card.style.cursor = 'pointer';
6269
card.addEventListener('click', (e) => {
6370
if (!(e.target instanceof HTMLAnchorElement)) {
64-
window.open(site, '_blank', 'noopener');
71+
window.open(live ? site : r.html_url, '_blank', 'noopener');
6572
}
6673
});
6774

@@ -73,38 +80,57 @@ <h3>
7380
const q = document.getElementById('q').value.toLowerCase().trim();
7481
const sort = document.getElementById('sort').value;
7582

76-
let list = repos;
83+
let list = repos.slice();
84+
if (!SHOW_ALL) {
85+
list = list.filter(r => r.has_pages || r.name.toLowerCase() === `${ORG.toLowerCase()}.github.io`);
86+
}
7787
if (q) {
78-
list = repos.filter(r =>
88+
list = list.filter(r =>
7989
(r.name && r.name.toLowerCase().includes(q)) ||
8090
(r.description && r.description.toLowerCase().includes(q))
8191
);
8292
}
93+
if (sort === 'name') list.sort((a,b) => a.name.localeCompare(b.name));
94+
else list.sort((a,b) => new Date(b.updated_at) - new Date(a.updated_at));
8395

84-
if (sort === 'name') {
85-
list.sort((a,b) => a.name.localeCompare(b.name));
86-
} else {
87-
list.sort((a,b) => new Date(b.updated_at) - new Date(a.updated_at));
88-
}
8996
render(list);
9097
}
9198

92-
(async function init() {
99+
async function loadAndRender() {
100+
const btn = document.getElementById('refreshBtn');
101+
if (btn) btn.disabled = true;
102+
93103
try {
94104
const repos = await fetchAllRepos();
95-
// Only show repos with Pages enabled OR the org root repo
96-
const withPages = repos.filter(r =>
97-
r.has_pages || r.name.toLowerCase() === `${ORG.toLowerCase()}.github.io`
98-
);
99-
window.__REPOS__ = withPages;
100-
filterSort(withPages);
101-
102-
document.getElementById('q').addEventListener('input', () => filterSort(window.__REPOS__));
103-
document.getElementById('sort').addEventListener('change', () => filterSort(window.__REPOS__));
105+
window.__REPOS__ = repos;
106+
filterSort(repos);
104107
} catch (err) {
105108
document.getElementById('grid').innerHTML =
106109
`<div class="empty">Error loading list: ${err.message}</div>`;
110+
} finally {
111+
if (btn) btn.disabled = false;
107112
}
113+
}
114+
115+
function addRefreshButtonOnce() {
116+
if (document.getElementById('refreshBtn')) return;
117+
const toolbar = document.querySelector('.toolbar');
118+
const btn = document.createElement('button');
119+
btn.id = 'refreshBtn';
120+
btn.textContent = 'Refresh';
121+
btn.style.cssText =
122+
'padding:10px 12px;border-radius:10px;border:1px solid #30363d;background:#0d1117;color:#e6edf3;cursor:pointer;';
123+
btn.addEventListener('click', loadAndRender);
124+
toolbar.appendChild(btn);
125+
}
126+
127+
(function start() {
128+
addRefreshButtonOnce();
129+
loadAndRender();
130+
document.getElementById('q').addEventListener('input', () => filterSort(window.__REPOS__ || []));
131+
document.getElementById('sort').addEventListener('change', () => filterSort(window.__REPOS__ || []));
132+
if (AUTO_REFRESH_MS > 0) setInterval(loadAndRender, AUTO_REFRESH_MS);
108133
})();
109134
</script>
110135

136+

0 commit comments

Comments
 (0)