我自己构建了Scrum Poker,因为其他的都很糟糕 🎴

发布: (2026年3月16日 GMT+8 19:03)
10 分钟阅读
原文: Dev.to

Source: Dev.to

(请提供您想要翻译的具体文本内容,我将为您翻译成简体中文并保持原有的格式、Markdown 语法以及技术术语不变。)

问题 😩

Sprint 计划。你的团队需要对任务进行估算。有人分享了一个 Planning Poker 链接。你点击进去。你等着。半数团队成员连不上。另一半已经去喝咖啡了。

听起来熟悉吗?

AGG TEAM 我们尝试了所有办法:

  • Planning Poker Online – 卡顿、广告、古老的 UI
  • Scrum Poker for Jira – 昂贵,需要 Jira
  • PlanITpoker – 能用,但缺少 我们 的功能

在第三次估算中途崩溃后,我想:“算了,我自己来搞一个。”

真正的挑战:6个团队,1个房间 🤯

我们没有一个 Scrum 团队——我们有 六个部门

  1. 前端
  2. 后端
  3. DevOps
  4. QA
  5. 分析
  6. 管理

每个人都想在同一个房间 同时 进行 Planning Poker。这简直是混乱——就像在同一张桌子上玩六种不同的纸牌游戏。现有工具只会说 “这里只有一个房间,自己想办法!”——这并不理想。

我构建的功能

🎰 多表支持(最多 6 张!)

核心概念:一个会话,多个相互独立的表。

interface Table {
  id: string;
  name: string;        // "Frontend Squad", "Backend Ninjas"
  revealed: boolean;    // Are cards revealed for this table?
}

interface Player {
  id: string;
  name: string;
  tableId: string;      // Which table they're at
  vote: string | null; // "5", "13", "?", "☕"
}
  • 主持人可以创建带自定义名称的表。
  • 每个人选择自己的表。
  • 每张表独立投票。
  • 你可以看到所有表 以及 一个整体平均值。

主持人权限: 随时添加、删除、重命名表;必要时在表之间移动成员。

⚡ 实时同步

使用 Supabase 作为后端,采用简单的轮询方式:

useEffect(() => {
  if (view === 'room' && roomId) {
    fetchRoomState();
    const interval = setInterval(fetchRoomState, 2000);
    return () => clearInterval(interval);
  }
}, [view, roomId]);

WebSocket 会更酷,但对原型和约 50 人的场景轮询已经足够。如果用户量增长,我会改用 WebSocket。

实时更新:

  • 有人加入 → 立即显示
  • 有人投票 → 立刻更新
  • 卡片翻开 → 所有人看到结果
  • 发送表情 → 在屏幕上飞过

💓 智能断线检测

其他工具的常见问题:用户关闭标签页后,头像仍然保留(幽灵用户)。

我的解决方案使用 心跳 + 显式断开

// Send heartbeat every 10 seconds
const sendHeartbeat = async () => {
  await fetch(`${API_URL}/rooms/${roomId}/heartbeat`, {
    method: 'POST',
    body: JSON.stringify({ playerId }),
  });
};

// Explicit disconnect on page close
window.addEventListener('beforeunload', () => {
  navigator.sendBeacon(
    `${API_URL}/rooms/${roomId}/disconnect`,
    JSON.stringify({ playerId })
  );
});

结果

  • 页面打开但闲置 → 只要不关闭就保持(不会被踢)
  • 关闭页面/标签页 → 立即断开(≈ 2 秒)

不再有幽灵用户,房间保持干净。

🎉 表情攻击!

点击任意参与者,投掷表情。表情会 真的从你的头像飞向对方,并伴随流畅动画。

可用表情:

👍 👏 🎉 ❤️ 🚀 🔥 😂 🤔 💯 ✨ 👌 🙌

使用场景

  • 👍 同意估算
  • 🔥 某人提出了绝佳观点
  • 😂 初级成员把一个简单任务估算成 89 点
  • ☕ 明显是喝咖啡时间

这让我们的计划会 变得有趣——大家现在真的期待估算会议了!

🃏 斐波那契 + 特殊卡

经典序列:0, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89

加上特殊卡:

  • ”?” – “我一点也不知道”
  • ”☕” – “需要咖啡才能思考”

你可以 在翻牌后仍然更改投票(大多数工具会锁定投票)。讨论过程中如果发现 13 实际上应该是 8——我的工具允许这样操作。

🧮 自动平均值计算

每张表都会显示其平均值。所有表的总体平均 会在底部统一展示。

const calculateAverage = (players: Player[], revealed: boolean) => {
  if (!revealed) return null;

  const numericVotes = players
    .map(p => p.vote)
    .filter(v => v && !isNaN(Number(v)))
    .map(Number);

  if (numericVotes.length === 0) return null;
  return (numericVotes.reduce((a, b) => a + b) / numericVotes.length).toFixed(1);
};

完美回答 “所有团队的整体估算是多少?” 这一问题。

🌙 暗黑模式 + 🌍 多语言

右下角的切换按钮可以切换主题(浅色/暗色) 以及 语言(EN/RU)。设置会保存到 localStorage,下次访问时自动生效。

因为如果你的应用在 2026 年还没有暗黑模式……你到底在干什么?


技术栈 🛠

前端

  • React + TypeScript
  • Tailwind CSS v4
  • Lucide 图标

后端

  • Supabase Edge Functions (Hono + Deno)
  • Supabase KV 存储(键值表)

为什么选择 Supabase?

  • 快速搭建 — 无需服务器部署
  • Edge Functions 让我可以编写 TypeScript 后端逻辑
  • 简单的 KV 存储用于房间状态
  • 免费层已足以满足原型需求

挑战与解决方案 💡

  1. 幽灵用户

    • 问题:用户关闭标签页,头像仍然残留。
    • 解决方案:心跳 + 使用 navigator.sendBeacon 显式断开连接。
  2. Z‑Index 争夺战

    • 问题:主题切换覆盖了模态框。
    • 解决方案:定义清晰的层级结构(按钮使用 z-30,模态框使用 z-40)。
  3. 状态管理

    • 问题:保持 6 张表格 + 玩家同步。
    • 解决方案:后端单一数据源;轮询获取更新。

规划的改变

之前

  • “等等,大家都加载好了吗?”
  • “刷新一下,我没看到你的投票。”
  • “还有谁在?”
  • 尴尬的等待

之后

  1. 创建房间(≈ 5 秒)
  2. 所有人加入(瞬间)
  3. 投票 → 揭示 → 讨论
  4. 发送表情包玩乐
  5. 按时结束

真实反馈

“等等,计划扑克也能这么顺畅?”
“我喜欢向大家投掷火焰表情!”
“终于有个不会让我愤怒退出的工具了。”

接下来是什么? 🚀

  • 会话历史(谁投了什么)
  • 可选投票计时器
  • 可导出报告(CSV/JSON)
  • 更多语言支持

为六支团队、一个房间以及大量咖啡而构建。

# Countdown

- Custom decks (T‑shirt sizes?)
- Sound effects (optional)
- Jira integration

---

关键要点 🧠

1. 构建你需要的东西
别再等 完美 的工具了。80 % 的功能在一个周末完成,胜过 100 % 的功能迟迟“最终实现”。

2. 轮询并非邪恶
WebSocket 很酷,但轮询对小团队来说非常好用。不要过度设计。

3. 小的惊喜很重要
表情符号功能只用了 2 小时,却成了大家的最爱。细小的东西能带来巨大的差异。

4. 暗色模式是必需的
说真的。已经 2026 年了。

试一试! 🎮

AGG POKER – 让团队通过交互式 Scrum 估算扑克工具进行协作估算,用户可以加入会话,简化项目规划。

scrumpoker.aggone.dev

免费使用

结论

有时只需几个晚上的编码,就能省去数月的挫折感。而且还能学到新东西,双赢。

如果你的团队也有相同的计划扑克痛点——自己动手构建吧!我们现在拥有出色的框架、免费托管以及 AI 助手,毫无借口。

有疑问?想法?想贡献? 留下评论吧!

附言 是的,企业级解决方案是存在的。我的方案成本为 $0,运行完美,且构建过程很有趣。 😄

0 浏览
Back to Blog

相关文章

阅读更多 »

Luminary:第2周 — 构建核心

!Luminary:第2周 — 构建核心的封面图片https://media2.dev.to/dynamic/image/width=1000,height=420,fit=cover,gravity=auto,format=auto/https%3A%2F%2F...

Meta 不再放弃 Jemalloc

- Meta 认识到 jemalloc 作为高性能内存分配器在其软件基础设施中的长期收益。 - 我们正在重新聚焦 jemalloc,……