A real-time planning poker (scrum poker) application for agile teams to estimate story points collaboratively.
Spoker enables distributed teams to conduct planning poker sessions in real-time. Team members join rooms, vote on story points, and see results synchronized across all participants. The application uses Firebase Realtime Database for instant updates and supports multiple user roles with different permissions.
- Real-time collaboration: Multiple users can join rooms and vote simultaneously with live updates
- Role-based access: Three roles (owner, participant, observant) with different capabilities
- Task management: Queue-based workflow with completed task history
- Vote hiding: Configurable emoji-based labels to hide votes until all participants vote
- Room privacy: Support for public and password-protected private rooms
- Email verification: Required authentication with email verification
- Persistent user profiles or voting history across sessions
- Integration with project management tools (Jira, Trello, etc.)
- Analytics or reporting beyond basic vote averages
- Mobile native applications (web-only)
- Offline mode or local-first synchronization
graph TB
subgraph "Client (Next.js)"
UI[React Components]
Store[Zustand Stores]
Services[Firebase Services]
end
subgraph "Firebase Services"
Auth[Firebase Auth]
DB[(Realtime Database)]
AppCheck[App Check]
end
subgraph "External"
Sentry[Error Tracking]
Analytics[Umami Analytics]
end
UI --> Store
UI --> Services
Services --> Auth
Services --> DB
Services --> AppCheck
UI --> Sentry
UI --> Analytics
style DB fill:#4CAF50
style Auth fill:#FFA726
style Store fill:#7B68EE
sequenceDiagram
participant User
participant UI
participant Store
participant Service
participant Firebase
User->>UI: Join Room / Vote
UI->>Service: Call Firebase Service
Service->>Firebase: Write to Realtime DB
Firebase-->>Service: Update Confirmed
Firebase-->>Store: Real-time Listener Update
Store-->>UI: State Change
UI-->>User: UI Update
spoker/
βββ src/
β βββ lib/ # Core application logic
β β βββ components/ # Reusable UI components
β β βββ constants/ # Configuration constants
β β β βββ routes/ # Route definitions (public/private/restricted)
β β β βββ hide-label.ts # Vote hiding emoji options
β β βββ hooks/ # Custom React hooks
β β βββ layout/ # Layout components (header, footer, auth)
β β βββ models/ # Form validation schemas (Zod)
β β βββ pages/ # Page-level components
β β β βββ hall/ # Room creation/joining interface
β β β βββ room/ # Main voting interface
β β β βββ home/ # Landing page
β β βββ services/ # Firebase service layer
β β β βββ firebase/
β β β βββ auth/ # Authentication operations
β β β βββ room/ # Room CRUD and updates
β β β βββ rules.ts # Security rules generator
β β βββ stores/ # Zustand state management
β β βββ styles/ # Theme configuration (Chakra UI)
β β βββ types/ # TypeScript type definitions
β β βββ utils/ # Utility functions
β βββ pages/ # Next.js pages (routing)
βββ public/ # Static assets
βββ tools/ # Build-time scripts
βββ config files # Next.js, TypeScript, Biome, etc.
| Package | Purpose | Version |
|---|---|---|
next |
React framework with SSR/SSG | ^16.1.1 |
react |
UI library | ^19.2.3 |
firebase |
Backend (Auth + Realtime Database) | ^12.7.0 |
@chakra-ui/react |
Component library | ^2.10.9 |
zustand |
State management | ^5.0.9 |
zod |
Runtime type validation | ^4.2.1 |
react-hook-form |
Form handling | ^7.69.0 |
- Biome: Linting and formatting (replaces ESLint/Prettier)
- TypeScript: Type safety
- Commitlint: Conventional commits
- Husky: Git hooks
- Knip: Unused code detection
- Sentry: Error tracking and monitoring
- Node.js v24.11.x (specified in
engines) - pnpm v10.24.0 (specified in
packageManager) - Firebase project with Realtime Database enabled
- ReCAPTCHA v3 site key (optional, for App Check)
Create a .env.local file with the following variables:
# Firebase Configuration
NEXT_PUBLIC_FIREBASE_API_KEY=
NEXT_PUBLIC_FIREBASE_AUTH_DOMAIN=
NEXT_PUBLIC_FIREBASE_DATABASE_URL=
NEXT_PUBLIC_FIREBASE_PROJECT_ID=
NEXT_PUBLIC_FIREBASE_STORAGE_BUCKET=
NEXT_PUBLIC_FIREBASE_MESSAGING_SENDER_ID=
NEXT_PUBLIC_FIREBASE_APP_ID=
NEXT_PUBLIC_FIREBASE_MEASUREMENT_ID=
# Optional: ReCAPTCHA for App Check
NEXT_PUBLIC_RECAPTCHA_SITE_KEY=
# Optional: Sentry
SENTRY_DSN=
NEXT_PUBLIC_SENTRY_DSN=
APP_ENV=# Install dependencies
pnpm install
# Run development server
pnpm devOpen http://localhost:3000 to view the application.
# Type check
pnpm type:check
# Lint and format
pnpm biome:check
pnpm biome:fix
# Build for production
pnpm build
# Start production server
pnpm start- Create a Firebase project in the Firebase Console
- Enable Realtime Database (not Firestore)
- Configure Authentication (Email/Password and Google providers)
- Deploy security rules using
pnpm generate-rules(generates rules fromsrc/lib/services/firebase/rules.ts) - Optionally enable App Check with ReCAPTCHA v3
# Run all checks (linting, type checking)
pnpm check:turbo
# Check for unused code
pnpm check:unusedThis project uses Conventional Commits. Use pnpm commit (via Commitizen) or follow the format:
type(scope): subject
body (optional)
footer (optional)
# Create release (updates version, CHANGELOG.md)
pnpm release
# Push release tags
pnpm push-release- Owner: Can create rooms, finish votes, configure room settings, manage tasks
- Participant: Can vote on tasks, view results when revealed
- Observant: Can view votes and results but cannot vote
- Creation: Owner creates room with name, privacy, and optional password
- Joining: Users join with a role (owner/participant/observant)
- Voting: Participants vote on current task; votes hidden until all vote
- Reveal: When all participants vote, results are shown to all
- Completion: Owner selects final estimate and moves to next task in queue
Votes can be hidden using emoji labels (monkey, chicken, cow, fish, money, cloud, shrimp, think) until all participants have voted. This prevents bias in estimation.
- Current Task: Active task being estimated (
room.task) - Queue: Upcoming tasks (
room.queue[]) - Completed: Finished tasks with estimates (
room.completed[])
- Create component in
src/lib/pages/[page-name]/index.tsx - Create route in
src/pages/[page-name].ts(or[page-name]/[id].tsfor dynamic routes) - Add route to appropriate constant in
src/lib/constants/routes/
- Create service function in
src/lib/services/firebase/[domain]/[operation]/index.ts - Export from service module
- Update Firebase rules in
src/lib/services/firebase/rules.tsif needed - Run
pnpm generate-rulesto regenerate rules JSON
- Room state:
src/lib/stores/room.ts - Auth state:
src/lib/stores/auth.ts - Use
useShallowfrom Zustand to prevent unnecessary re-renders
Currently, the project does not include automated tests. Manual testing should cover:
- Room creation and joining
- Real-time vote synchronization
- Role-based permissions
- Task queue management
- Email verification flow
- Private room password protection
The project is configured for Vercel deployment (vercel.json). Ensure environment variables are set in the deployment platform.
Thanks goes to these wonderful people (emoji key):
Agustinus Nathaniel π» π π¨ π€ π π§ |
This project follows the all-contributors specification. Contributions of any kind welcome!
- CONTRIBUTING.md - Contribution guidelines
- SPEC.md - System specification and invariants
- AGENTS.md - AI agent guidance and mental model