A planning poker application built with Next.js and React.
Have an idea you want to suggest? Pull requests are welcome.
- Clone and install dependencies.
git clone <url> .
npm install- Start local development.
npm run dev- Open
http://localhost:3000/.
npm run dev: start Next.js in development modenpm run build: production buildnpm run start: run the production server from a completed buildnpm run lint: run ESLintnpm run format: run Prettier across source and docs
Local production check:
npm run lint
npm run buildGitHub Actions build workflow is in .github/workflows/deploy.yml and currently runs:
npm ci --prefer-offline --no-auditnpm run lintnpm run build
Routing behavior is configured in next.config.mjs:
- Root deployment (default): do not set
DEPLOY_TARGET - Subpath deployment: set
DEPLOY_TARGET=<subpath>
Example for GitHub Pages style hosting:
DEPLOY_TARGET=planning-poker npm run buildThis applies both basePath and assetPrefix to /<DEPLOY_TARGET>.
The repo includes netlify.toml with the Essential Next.js plugin.
[build]
command = "npm run build"
publish = ".next"
[[plugins]]
package = "@netlify/plugin-nextjs"If deploying under a subpath, set DEPLOY_TARGET in Netlify build environment variables.
The app reads and writes directly to Supabase from the browser. It expects these public tables:
users: session members and theirlast_presenceusers: session members withis_spectatorrole andlast_presencescores: one score per user per sessionoptions: per-session settings (point sequence, stage, moderators, confirmations)rounds: historical snapshots saved when returning from results to voting
To recreate schema and policies, run supabase/schema.sql in the Supabase SQL editor.
The script will:
- recreate
users,scores,options, androunds - create
created_atandupdated_attimestamps and update triggers - apply keys/indexes needed by upsert and session filters
- enable RLS with permissive anon/authenticated policies
- add realtime publication for
scores,options, androunds
- Session id comes from URL hash (
/voting#abc1234) - Without a hash, app generates or redirects to a valid voting route
resultsandoptionsroutes require both hash session and a locally stored user for that session
User storage is session-aware in src/utils/userStorage.js:
- stores a preferred name profile
- stores per-session user memberships keyed by session hash
- avoids one session identity overwriting another session identity
Session state and rounds state use guarded refreshes in src/hooks/useSessionState.js and src/hooks/useRounds.js:
- subscriptions trigger canonical refetches
- stale async responses are ignored when session/request token no longer matches
- roster updates subscribe to
usersevents for faster convergence
- Confirm Node version matches CI expectation (
node-version: '24'in workflow). - Reinstall dependencies:
npm ci. - Run lint/build to surface issues quickly:
npm run lint
npm run build- Verify all clients are in the same hash session id.
- Confirm Supabase realtime includes
scores,options, androundstables. - Confirm
usersrows are being updated with recentlast_presence. - Refresh page to force a full state bootstrap.
- Confirm
optionstable exists and RLS allows read/write. - Confirm key names in table include expected values (
stage,point_sequence,confirm,moderators). - Check browser console for Supabase channel errors.
.
├── pages
├── src
├── static
├── supabase
├── eslint.config.js
├── next.config.mjs
├── netlify.toml
├── package-lock.json
├── package.json
└── README.md
pages: Next.js pages and API routessrc: application components and client-side API helpersstatic: static assetssupabase: SQL schema for the backing database