Skip to content
This repository was archived by the owner on Jun 13, 2026. It is now read-only.

Commit a77b2f6

Browse files
qiongyu1999claude
andcommitted
refactor(dashboard): simplify layout and add pagination
- Remove "Deduped Cards Posted" and "Historical Duplicates" stat cards - Remove "Historical Duplicate Bursts" panel - Remove "Open Cloudflare Pages Analytics" CTA button - Add pagination (20 per page) to Card Comments table showing all records - Remove "Dedupe" column from table since all are unique now Co-Authored-By: Claude Opus 4 <noreply@anthropic.com>
1 parent cbc9316 commit a77b2f6

1 file changed

Lines changed: 35 additions & 25 deletions

File tree

scripts/contributor-card-dashboard.ts

Lines changed: 35 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,6 @@ const SHARE_ANALYTICS_JSON = join(OUT_DIR, "share-analytics.json");
88
const OUTPUT_INDEX_HTML = join(OUT_DIR, "index.html");
99
const OUTPUT_HTML = join(OUT_DIR, "contributor-card-dashboard.html");
1010

11-
const PAGES_ANALYTICS_URL =
12-
"https://dash.cloudflare.com/64ad4569ffd912432d6b86d5656484c4/pages/view/open-design-landing/analytics";
1311
const DASHBOARD_PUBLIC_BASE = "https://nexu-io.github.io/open-design-bot-sandbox";
1412
const SHARE_ANALYTICS_JSON_PUBLIC_URL = `${DASHBOARD_PUBLIC_BASE}/share-analytics.json`;
1513
const SHARE_ANALYTICS_CSV_PUBLIC_URL = `${DASHBOARD_PUBLIC_BASE}/share-analytics.csv`;
@@ -109,13 +107,12 @@ function countRows(rows: Record<string, number>): string {
109107
.join("");
110108
}
111109

112-
function recentRows(events: CardEventRow[]): string {
113-
return events.slice(-12).reverse().map((event) => `<tr>
110+
function allRows(events: CardEventRow[]): string {
111+
return [...events].reverse().map((event) => `<tr>
114112
<td>${escapeHtml(event.createdAt.replace("T", " ").replace("Z", ""))}</td>
115113
<td>@${escapeHtml(event.recipient)}</td>
116114
<td>${escapeHtml(event.tierName)}</td>
117115
<td>${escapeHtml(event.surface)}</td>
118-
<td>${event.duplicateKind === "unique" ? "Unique" : "Historical duplicate"}</td>
119116
<td><a href="${escapeHtml(event.commentUrl)}">#${escapeHtml(event.threadNumber)}</a></td>
120117
</tr>`).join("");
121118
}
@@ -195,8 +192,7 @@ function renderShareAnalyticsPanel(
195192
${stat("Public JSON", "Not generated", "needs CF token", "缺 CF token")}
196193
${stat("Public CSV", "Not generated", "needs CF token", "缺 CF token")}
197194
</div>
198-
<p><a class="cta" href="${PAGES_ANALYTICS_URL}" target="_blank" rel="noreferrer">Open Cloudflare Pages Analytics &rarr;</a></p>
199-
</section>`;
195+
</section>`;
200196
}
201197
const win = analytics.windows.last30Days;
202198
const daily = win.daily;
@@ -227,7 +223,6 @@ function renderShareAnalyticsPanel(
227223
<li><a href="${SHARE_ANALYTICS_CSV_PUBLIC_URL}" target="_blank" rel="noreferrer"><code>${SHARE_ANALYTICS_CSV_PUBLIC_URL}</code></a> &mdash; <code>=IMPORTDATA(...)</code> in Google Sheets · Sheets 一行公式接入</li>
228224
<li>Refreshed hourly by <code>contributor-dashboard.yml</code> · 由 <code>contributor-dashboard.yml</code> 每小时刷新</li>
229225
</ul>
230-
<p><a class="cta" href="${PAGES_ANALYTICS_URL}" target="_blank" rel="noreferrer">Open Cloudflare Pages Analytics &rarr;</a></p>
231226
</section>`;
232227
}
233228

@@ -239,10 +234,6 @@ function main() {
239234
const events = parseCsv(readFileSync(EVENTS_CSV, "utf8")) as unknown as CardEventRow[];
240235
const summary = JSON.parse(readFileSync(SUMMARY_JSON, "utf8")) as Summary;
241236
const shareAnalytics = loadShareAnalytics();
242-
const duplicateRows = Object.entries(summary.duplicateRecipients)
243-
.sort((a, b) => b[1] - a[1])
244-
.map(([recipient, count]) => `<tr><td>@${escapeHtml(recipient)}</td><td>${count}</td><td>historical duplicate burst</td></tr>`)
245-
.join("");
246237

247238
const cardsWithShareLink = events.filter((event) => event.shareUrl).length;
248239
const shareAnalyticsPanel = renderShareAnalyticsPanel(shareAnalytics, cardsWithShareLink);
@@ -259,7 +250,7 @@ function main() {
259250
h1 { font-size: clamp(36px, 6vw, 72px); line-height:.95; letter-spacing:-.06em; margin:0 0 10px; }
260251
h2 { margin:0 0 18px; }
261252
p, small, .muted { color:#94a3b8; line-height:1.65; }
262-
.stats { display:grid; grid-template-columns:repeat(4, minmax(0, 1fr)); gap:16px; margin:24px 0; }
253+
.stats { display:grid; grid-template-columns:repeat(auto-fit, minmax(200px, 1fr)); gap:16px; margin:24px 0; }
263254
.stat, .panel { border:1px solid rgba(148,163,184,.24); border-radius:24px; background:linear-gradient(180deg, rgba(255,255,255,.08), rgba(255,255,255,.03)); box-shadow:0 24px 80px rgba(2,6,23,.38); }
264255
.stat { min-height:140px; padding:20px; display:flex; flex-direction:column; justify-content:space-between; }
265256
.stat span { color:#94a3b8; }
@@ -273,8 +264,10 @@ function main() {
273264
th, td { padding:11px 10px; border-bottom:1px solid rgba(148,163,184,.24); text-align:left; font-size:14px; }
274265
th { color:#94a3b8; }
275266
a { color:#22d3ee; text-decoration:none; }
276-
a.cta { display:inline-block; margin-top:8px; padding:12px 22px; border-radius:14px; background:linear-gradient(90deg, #22d3ee, #fbbf24); color:#0f172a; font-weight:700; letter-spacing:.01em; }
277-
a.cta:hover { filter:brightness(1.08); }
267+
.pagination { display:flex; align-items:center; gap:12px; margin-top:16px; justify-content:center; }
268+
.pagination button { padding:8px 16px; border:1px solid rgba(148,163,184,.4); border-radius:8px; background:rgba(255,255,255,.06); color:#f8fafc; cursor:pointer; font-size:13px; }
269+
.pagination button:hover { background:rgba(34,211,238,.15); border-color:#22d3ee; }
270+
.pagination span { color:#94a3b8; font-size:13px; }
278271
ul { margin:6px 0 14px 22px; padding:0; }
279272
@media (max-width:900px) { .stats, .grid { grid-template-columns:1fr; } main { width:min(100vw - 28px, 720px); } }
280273
</style>
@@ -286,16 +279,8 @@ function main() {
286279
<p class="muted">Generated 生成时间: ${escapeHtml(summary.generatedAt)}</p>
287280
288281
<section class="stats">
289-
${stat("Deduped Cards Posted", summary.dedupedCardEvents, "business reporting count", "用于汇报的去重口径")}
290282
${stat("Raw Cards Posted", summary.rawCardComments, "visible bot comments", "真实可见 bot 评论数")}
291283
${stat("Unique Recipients", summary.uniqueRecipients, "contributors who received a card", "收到卡片的去重贡献者")}
292-
${stat("Historical Duplicates", summary.historicalDuplicateCount, "duplicate burst events excluded from headline metrics", "已从核心指标排除的历史重复卡")}
293-
</section>
294-
295-
<section class="panel">
296-
<h2>Historical Duplicate Bursts 历史重复发卡</h2>
297-
<p class="muted">These rows came from the old workflow race condition. They remain visible on GitHub for audit, but are excluded from deduped headline metrics.<br>这些记录来自旧 workflow 并发问题。GitHub 上仍可见用于审计,但不会进入去重后的核心汇报指标。</p>
298-
<table><thead><tr><th>Recipient</th><th>Duplicate cards</th><th>Reason</th></tr></thead><tbody>${duplicateRows || `<tr><td colspan="3">No duplicate burst detected.</td></tr>`}</tbody></table>
299284
</section>
300285
301286
<section class="grid">
@@ -306,9 +291,34 @@ function main() {
306291
${shareAnalyticsPanel}
307292
308293
<section class="panel">
309-
<h2>Recent Card Comments 最近的卡片评论</h2>
310-
<table><thead><tr><th>Time</th><th>Recipient</th><th>Tier</th><th>Surface</th><th>Dedupe</th><th>Thread</th></tr></thead><tbody>${recentRows(events)}</tbody></table>
294+
<h2>Card Comments 卡片评论(共 ${events.length} 条)</h2>
295+
<div id="card-table-wrap">
296+
<table><thead><tr><th>Time</th><th>Recipient</th><th>Tier</th><th>Surface</th><th>Thread</th></tr></thead><tbody id="card-tbody">${allRows(events)}</tbody></table>
297+
</div>
298+
<div class="pagination" id="pagination"></div>
311299
</section>
300+
<script>
301+
(function(){
302+
const PAGE_SIZE = 20;
303+
const tbody = document.getElementById('card-tbody');
304+
const allRows = Array.from(tbody.querySelectorAll('tr'));
305+
const totalPages = Math.ceil(allRows.length / PAGE_SIZE);
306+
let currentPage = 1;
307+
function render() {
308+
const start = (currentPage - 1) * PAGE_SIZE;
309+
const end = start + PAGE_SIZE;
310+
allRows.forEach((row, i) => { row.style.display = (i >= start && i < end) ? '' : 'none'; });
311+
const pag = document.getElementById('pagination');
312+
let html = '';
313+
if (currentPage > 1) html += '<button onclick="window.__goPage(' + (currentPage - 1) + ')">← 上一页</button>';
314+
html += '<span>第 ' + currentPage + ' / ' + totalPages + ' 页</span>';
315+
if (currentPage < totalPages) html += '<button onclick="window.__goPage(' + (currentPage + 1) + ')">下一页 →</button>';
316+
pag.innerHTML = html;
317+
}
318+
window.__goPage = function(p) { currentPage = p; render(); document.getElementById('card-table-wrap').scrollIntoView({behavior:'smooth'}); };
319+
render();
320+
})();
321+
</script>
312322
</main>
313323
</body>
314324
</html>`;

0 commit comments

Comments
 (0)