feat(rchoices): add CrowdSurf tab to choices dashboard

Adds a fourth sub-tab linking to the CrowdSurf module with teaser content.
Bumps JS cache version to v=3.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Jeff Emmett 2026-03-16 16:37:31 -07:00
parent dae8f72acb
commit 077bcf260a
2 changed files with 17 additions and 4 deletions

View File

@ -31,7 +31,7 @@ class FolkChoicesDashboard extends HTMLElement {
private space: string;
/* Demo state */
private demoTab: "spider" | "ranking" | "voting" = "spider";
private demoTab: "spider" | "ranking" | "voting" | "crowdsurf" = "spider";
private hoveredPerson: string | null = null;
private rankItems: { id: number; name: string; emoji: string }[] = [];
private rankDragging: number | null = null;
@ -503,15 +503,17 @@ class FolkChoicesDashboard extends HTMLElement {
}
private renderDemo() {
const tabs: { key: "spider" | "ranking" | "voting"; label: string; icon: string }[] = [
const tabs: { key: "spider" | "ranking" | "voting" | "crowdsurf"; label: string; icon: string }[] = [
{ key: "spider", label: "Spider Chart", icon: "🕸" },
{ key: "ranking", label: "Ranking", icon: "📊" },
{ key: "voting", label: "Live Voting", icon: "☑" },
{ key: "crowdsurf", label: "CrowdSurf", icon: "🏄" },
];
let content = "";
if (this.demoTab === "spider") content = this.renderSpider();
else if (this.demoTab === "ranking") content = this.renderRanking();
else if (this.demoTab === "crowdsurf") content = this.renderCrowdSurf();
else content = this.renderVoting();
this.shadow.innerHTML = `
@ -725,6 +727,17 @@ class FolkChoicesDashboard extends HTMLElement {
</div>`;
}
private renderCrowdSurf(): string {
return `<div style="text-align:center;padding:2rem 1rem;">
<div style="font-size:3rem;margin-bottom:0.75rem;">🏄</div>
<h3 style="color:var(--rs-text-primary);margin:0 0 0.5rem;">CrowdSurf</h3>
<p style="color:var(--rs-text-secondary);margin:0 0 1.5rem;max-width:360px;margin-inline:auto;">
Swipe-based community coordination. Propose activities, set commitment thresholds, and watch them trigger when enough people join.
</p>
<a href="/${this.space}/crowdsurf" style="display:inline-block;padding:0.6rem 1.5rem;background:var(--rs-primary);color:#fff;border-radius:8px;text-decoration:none;font-weight:600;font-size:0.9rem;">Open CrowdSurf</a>
</div>`;
}
/* -- Demo event binding -- */
private bindDemoEvents() {
@ -732,7 +745,7 @@ class FolkChoicesDashboard extends HTMLElement {
// Tab switching
this.shadow.querySelectorAll<HTMLButtonElement>(".demo-tab").forEach((btn) => {
btn.addEventListener("click", () => {
const tab = btn.dataset.tab as "spider" | "ranking" | "voting";
const tab = btn.dataset.tab as "spider" | "ranking" | "voting" | "crowdsurf";
if (tab && tab !== this.demoTab) {
this.demoTab = tab;
this.renderDemo();

View File

@ -56,7 +56,7 @@ routes.get("/", (c) => {
modules: getModuleInfoList(),
theme: "dark",
body: `<folk-choices-dashboard space="${spaceSlug}"></folk-choices-dashboard>`,
scripts: `<script type="module" src="/modules/rchoices/folk-choices-dashboard.js?v=2"></script>`,
scripts: `<script type="module" src="/modules/rchoices/folk-choices-dashboard.js?v=3"></script>`,
styles: `<link rel="stylesheet" href="/modules/rchoices/choices.css">`,
}));
});