Skip to content

Commit 72ab2f1

Browse files
authored
Update index.html
1 parent ebb2e94 commit 72ab2f1

1 file changed

Lines changed: 94 additions & 127 deletions

File tree

docs/index.html

Lines changed: 94 additions & 127 deletions
Original file line numberDiff line numberDiff line change
@@ -1,143 +1,110 @@
1-
<!doctype html>
2-
<html lang="en">
3-
<head>
4-
<meta charset="utf-8" />
5-
<title>Democracy Lab — Project Sites</title>
6-
<meta name="viewport" content="width=device-width, initial-scale=1" />
7-
<style>
8-
:root { --max: 1100px; }
9-
body { font-family: Arial, Helvetica, sans-serif; margin: 0; background:#0e1116; color:#e6edf3; }
10-
header { padding: 24px; text-align:center; }
11-
header h1 { margin: 0 0 8px; font-size: 28px; }
12-
header p { margin: 0; opacity:.8 }
13-
.container { max-width: var(--max); margin: 0 auto; padding: 16px 24px 48px; }
14-
.toolbar { display:flex; gap:12px; align-items:center; margin-bottom:16px; flex-wrap:wrap; }
15-
input[type="search"] { flex:1; padding:10px 12px; border-radius:10px; border:1px solid #30363d; background:#0d1117; color:#e6edf3; }
16-
select { padding:10px 12px; border-radius:10px; border:1px solid #30363d; background:#0d1117; color:#e6edf3; }
17-
.grid { display:grid; grid-template-columns: repeat(auto-fill, minmax(260px, 1fr)); gap:16px; }
18-
.card { background:#0d1117; border:1px solid #30363d; border-radius:14px; padding:16px; display:flex; flex-direction:column; gap:8px; }
19-
.card h3 { margin:0; font-size:18px; }
20-
.meta { font-size:12px; opacity:.8 }
21-
.links a { text-decoration:none; color:#67b7ff; margin-right:10px; }
22-
footer { text-align:center; padding:24px; opacity:.7; }
23-
.empty { text-align:center; padding:40px; opacity:.8 }
24-
</style>
25-
</head>
26-
<body>
27-
<header>
28-
<h1>Democracy Lab — Project Sites</h1>
29-
<p>Auto-listing of organization repositories that publish with GitHub Pages.</p>
30-
</header>
1+
<script>
2+
// ---- CONFIG ----
3+
const ORG = 'Democracy-Lab';
4+
const ORG_PAGES_BASE = 'https://democracy-lab.github.io'; // base org pages host
315

32-
<div class="container">
33-
<div class="toolbar">
34-
<input id="q" type="search" placeholder="Search repositories… (name or description)" />
35-
<select id="sort">
36-
<option value="updated">Sort: Recently updated</option>
37-
<option value="name">Sort: Name (A→Z)</option>
38-
</select>
39-
</div>
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+
const all = [];
9+
while (url) {
10+
const res = await fetch(url);
11+
if (!res.ok) throw new Error(`GitHub API: ${res.status} ${res.statusText}`);
12+
const page = await res.json();
13+
all.push(...page);
14+
// parse Link header for pagination
15+
const link = res.headers.get('Link');
16+
const next = link && link.split(',').find(s => s.includes('rel="next"'));
17+
url = next ? next.split(';')[0].trim().slice(1, -1) : null;
18+
}
19+
return all;
20+
}
4021

41-
<div id="grid" class="grid"></div>
42-
<div id="empty" class="empty" style="display:none;">No matches.</div>
43-
</div>
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+
}
4429

45-
<footer>Built from the public GitHub API • Last refreshed client-side</footer>
30+
function fmtDate(iso) {
31+
try { return new Date(iso).toLocaleString(); } catch { return iso; }
32+
}
4633

47-
<script>
48-
// ---- CONFIG ----
49-
const ORG = 'Democracy-Lab';
50-
const ORG_PAGES_BASE = 'https://democracy-lab.github.io'; // lowercased host is fine
34+
function render(list) {
35+
const grid = document.getElementById('grid');
36+
const empty = document.getElementById('empty');
37+
grid.innerHTML = '';
38+
if (!list.length) { empty.style.display = ''; return; }
39+
empty.style.display = 'none';
5140

52-
// Fetch all org repos (handles pagination if >100)
53-
async function fetchAllRepos(url = `https://api.github.com/orgs/${ORG}/repos?per_page=100&type=public`) {
54-
const all = [];
55-
while (url) {
56-
const res = await fetch(url);
57-
if (!res.ok) throw new Error(`GitHub API: ${res.status} ${res.statusText}`);
58-
const page = await res.json();
59-
all.push(...page);
60-
// parse Link header for pagination
61-
const link = res.headers.get('Link');
62-
const next = link && [...link.split(',')].find(s => s.includes('rel="next"'));
63-
url = next ? next.split(';')[0].trim().slice(1, -1) : null;
64-
}
65-
return all;
66-
}
41+
for (const r of list) {
42+
const site = pagesUrl(r);
6743

68-
// Convert repo → Pages URL (works for org project pages)
69-
function pagesUrl(repo) {
70-
// org root site is <org>.github.io; project sites are under /<repo>
71-
if (repo.name.toLowerCase() === `${ORG.toLowerCase()}.github.io`) {
72-
return `${ORG_PAGES_BASE}/`;
73-
}
74-
return `${ORG_PAGES_BASE}/${encodeURIComponent(repo.name)}/`;
75-
}
44+
const card = document.createElement('div');
45+
card.className = 'card';
46+
card.innerHTML = `
47+
<h3>
48+
<a href="${site}" target="_blank" rel="noopener" style="color:#67b7ff;text-decoration:none;">
49+
${r.name}
50+
</a>
51+
</h3>
52+
<div class="meta">${r.description ? r.description : ''}</div>
53+
<div class="meta">Updated: ${fmtDate(r.updated_at)}</div>
54+
<div class="links">
55+
<a href="${site}" target="_blank" rel="noopener">View site</a>
56+
<a href="${r.html_url}" target="_blank" rel="noopener">Repo</a>
57+
</div>
58+
`;
7659

77-
function fmtDate(iso) {
78-
try { return new Date(iso).toLocaleString(); } catch { return iso; }
60+
// Make the whole card open the site when clicked (but not when clicking an inner link)
61+
card.style.cursor = 'pointer';
62+
card.addEventListener('click', (e) => {
63+
if (!(e.target instanceof HTMLAnchorElement)) {
64+
window.open(site, '_blank', 'noopener');
65+
}
66+
});
67+
68+
grid.appendChild(card);
7969
}
70+
}
8071

81-
function render(list) {
82-
const grid = document.getElementById('grid');
83-
const empty = document.getElementById('empty');
84-
grid.innerHTML = '';
85-
if (!list.length) { empty.style.display = ''; return; }
86-
empty.style.display = 'none';
72+
function filterSort(repos) {
73+
const q = document.getElementById('q').value.toLowerCase().trim();
74+
const sort = document.getElementById('sort').value;
8775

88-
for (const r of list) {
89-
const card = document.createElement('div');
90-
card.className = 'card';
91-
card.innerHTML = `
92-
<h3>${r.name}</h3>
93-
<div class="meta">${r.description ? r.description : ''}</div>
94-
<div class="meta">Updated: ${fmtDate(r.updated_at)}</div>
95-
<div class="links">
96-
<a href="${pagesUrl(r)}" target="_blank" rel="noopener">View site</a>
97-
<a href="${r.html_url}" target="_blank" rel="noopener">Repo</a>
98-
</div>
99-
`;
100-
grid.appendChild(card);
101-
}
76+
let list = repos;
77+
if (q) {
78+
list = repos.filter(r =>
79+
(r.name && r.name.toLowerCase().includes(q)) ||
80+
(r.description && r.description.toLowerCase().includes(q))
81+
);
10282
}
10383

104-
function filterSort(repos) {
105-
const q = document.getElementById('q').value.toLowerCase().trim();
106-
const sort = document.getElementById('sort').value;
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+
}
89+
render(list);
90+
}
10791

108-
let list = repos;
109-
if (q) {
110-
list = repos.filter(r =>
111-
(r.name && r.name.toLowerCase().includes(q)) ||
112-
(r.description && r.description.toLowerCase().includes(q))
113-
);
114-
}
92+
(async function init() {
93+
try {
94+
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);
115101

116-
if (sort === 'name') {
117-
list.sort((a,b) => a.name.localeCompare(b.name));
118-
} else {
119-
list.sort((a,b) => new Date(b.updated_at) - new Date(a.updated_at));
120-
}
121-
render(list);
102+
document.getElementById('q').addEventListener('input', () => filterSort(window.__REPOS__));
103+
document.getElementById('sort').addEventListener('change', () => filterSort(window.__REPOS__));
104+
} catch (err) {
105+
document.getElementById('grid').innerHTML =
106+
`<div class="empty">Error loading list: ${err.message}</div>`;
122107
}
108+
})();
109+
</script>
123110

124-
(async function init() {
125-
try {
126-
const repos = await fetchAllRepos();
127-
// Only show repos with Pages enabled OR the org root repo
128-
const withPages = repos.filter(r =>
129-
r.has_pages || r.name.toLowerCase() === `${ORG.toLowerCase()}.github.io`
130-
);
131-
window.__REPOS__ = withPages;
132-
filterSort(withPages);
133-
134-
document.getElementById('q').addEventListener('input', () => filterSort(window.__REPOS__));
135-
document.getElementById('sort').addEventListener('change', () => filterSort(window.__REPOS__));
136-
} catch (err) {
137-
document.getElementById('grid').innerHTML =
138-
`<div class="empty">Error loading list: ${err.message}</div>`;
139-
}
140-
})();
141-
</script>
142-
</body>
143-
</html>

0 commit comments

Comments
 (0)