
Предыстория, или "Ну сколько можно терпеть?"
Знаете это чувство, когда сидишь на очередном спринт-планировании, команда пытается оценить таски, а планинг покер тормозит так, что успеваешь кофе сварить, пока карточка загрузится?
Вот у меня в AGG TEAM такая же история была. Мы пробовали:
Planning Poker Online - лагает, реклама, UI из 2010-го
Scrum Poker for Jira - платный, тяжелый, нужен Jira (сюрприз!)
PlanITpoker - работает, но без фич, которые нужны именно нам
И тут я подумал: "А че, сам накодить не судьба?"
Спойлер: судьба, но получилось годно! 🚀
Проблема: когда у тебя 6 команд и один покер 😅
У нас в компании не одна команда, а шесть подразделений. Представьте: все хотят планировать одновременно, но в одной комнате покера. Это как пытаться играть в карты в переполненном баре - хаос и непонятки.
Нужно было:
✅ Несколько столов одновременно (до 6)
✅ Каждый стол со своей оценкой
✅ Общая оценка по всем столам
✅ Real-time синхронизация (не в режиме "обнови страницу")
✅ Кидаться эмодзи друг в друга (это важно!)
✅ Темная тема (это ж 2026-й на дворе!)
✅ Мультиязычность (RU/EN)
Что получилось: Scrum Poker AGG TEAM

Фичи, которыми я горжусь 💪
1. Multi-table режим 🎰
Создаёшь сессию → выбираешь до 6 столов → каждый выбирает свой стол → все видят всех. Красота!
// Каждый стол имеет свой ID, название и статус interface Table { id: string; name: string; revealed: boolean; }
Хост может управлять столами на лету: добавлять, удалять, переименовывать. Остальные просто переключаются между ними.
2. Real-time всего и вся ⚡
Использовал Supabase как бэкенд. Polling каждые 2 секунды -тне элегантно, но работает стабильно:
useEffect(() => { if (view === 'room' && roomId) { fetchRoomState(); const interval = setInterval(fetchRoomState, 2000); return () => clearInterval(interval); } }, [view, roomId]);
Да, WebSocket'ы были бы круче, но для прототипа и 50 человек polling - самое то. Если масштаб вырастет, переделаю (наверное 😄).
3. Emoji-атаки! 🎉👍🔥
Можно кидаться эмодзи прямо в других участников! Анимация летит от твоего аватара к цели. Это добавило геймификации и веселья на скучные планирования.
12 эмодзи на выбор: 👍 👏 🎉 ❤️ 🚀 🔥 😂 🤔 💯 ✨ 👌 🙌
const throwAtPlayer = async (targetPlayerId: string, emoji: string) => { await fetch(`${API_URL}/rooms/${roomId}/throw`, { method: 'POST', body: JSON.stringify({ fromPlayerId: playerId, toPlayerId: targetPlayerId, emoji, }), }); };
4. Карты Фибоначчи + дополнительные 🃏
Классика: 0, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, плюс "?" (не знаю) и "☕" (нужен кофе-брейк).
Можно менять голос даже после раскрытия - удобно, когда обсудили и хочешь скорректировать оценку.
5. Автоматический расчёт среднего 🧮
Для каждого стола + общая оценка по всем столам внизу. Считаются только числовые карты:
const calculateAverage = (tablePlayers: Player[], revealed: boolean) => { if (!revealed) return null; const numericVotes = tablePlayers .map(p => p.vote) .filter(v => v && !isNaN(Number(v))) .map(v => Number(v)); if (numericVotes.length === 0) return null; const sum = numericVotes.reduce((a, b) => a + b, 0); return (sum / numericVotes.length).toFixed(1); };
6. Темная тема + мультиязычность 🌙🌍
Переключатели в правом верхнем углу. Язык и тема сохраняются в localStorage:
const { t, language, toggleLanguage } = useTranslation(); // Все тексты через переводы <h1>{t.welcome.title}</h1>
Перевёл весь интерфейс: все кнопки, сообщения, toast-уведомления.
Технический стек 🛠
Frontend: React + TypeScript + Tailwind CSS v4
Backend: Supabase Edge Functions (Hono framework)
Database: Supabase KV Store (key-value таблица)
Hosting: Supabase (всё в одном месте)
State Management: React Hooks (useState, useEffect, useCallback)
Icons: Lucide React
Notifications: Sonner (toast messages)
Почему Supabase?
Быстрый старт — не нужно поднимать свой сервер
Edge Functions — бэкенд на TypeScript/Deno прямо рядом с фронтом
KV Store — простая табличка для хранения состояния комнат
Auth из коробки — если понадобится добавить авторизацию
Бесплатный tier — для прототипа достаточно
Архитектурные решения 🏗
1. Трёхслойная архитектура
Frontend (React) ↕️ fetch API Server (Hono on Deno) ↕️ SQL/KV Database (Supabase Postgres + KV)
Фронт общается только с сервером, сервер - с базой. Классика.
2. KV Store вместо SQL
Комнаты хранятся как JSON в key-value таблице:
// Сохранение комнаты await kv.set(`room:${roomId}`, { id: roomId, createdAt: Date.now(), hostId: hostPlayerId, tables: [...], emojis: [...], }); // Получение комнаты const room = await kv.get(`room:${roomId}`);
Для игроков - отдельные ключи:
await kv.set(`room:${roomId}:players`, players);
Просто, быстро, без миграций!
3. Polling вместо WebSockets
Да, не самое модное решение, но:
✅ Проще в отладке
✅ Не нужно управлять соединениями
✅ Работает через любые прокси
❌ Чуть больше нагрузки на сервер
Для команд до 100 человек - норм. Если вырастет - добавлю Supabase Realtime.
С чем пришлось повозиться 😤
1. "А где мои игроки?!"
Первая версия не кикала неактивных. Люди закрывали вкладку, а их аватары висели. Добавил heartbeat - стало гораздо лучше.
2. Переводы всего подряд
Когда добавлял мультиязычность, понял, что хардкод-текста было ВЕЗДЕ. Пришлось вынести в отдельный файл translations.ts и заменить 100+ строк на t.welcome.title, t.toast.error и т.д.
3. Tailwind CSS v4
Использовал новую версию - синтаксис немного и��менился. Пришлось читать документацию вместо копипаста со Stack Overflow 😅
Что дальше? 🚀
План на будущее:
WebSocket вместо polling (когда надоест)
История сессий (сколько оценили, кто как голосовал)
Таймер для голосования
Звуковые уведомления (можно отключить)
Кастомные колоды (не только Фибоначчи)
Интеграция с Jira/Linear (автоматическая оценка тасков)
Попробуйте сами! 🎮
https://scrumpoker.aggone.dev/
Выводы 🧠
Не бойтесь писать свои решения - иногда проще сделать самому, чем искать идеальное
Polling - не всегда зло - для малых команд вполне рабочее решение
Supabase - огонь для быстрых прототипов
Геймификация работает - эмодзи реально оживили планирования
Темная тема - must have - уже 2026-й, ребят!
P.S.
Если вы тоже страдаете от корпоративных инструментов, которые тормозят и бесят - welcome в клуб "Сделаю сам". Иногда выходные, потраченные на свой велосипед, экономят месяцы нервов в будущем 😉
А можете пользоваться и моим решением. Оно бесплатное!
Всех благ!
Вопросы? Идеи? Баги? Пишите в комментариях!
