This monorepo implements a smart polling application with conditional questions, anonymous participation, and scheduling. It uses Next.js + Tailwind on the frontend and Convex for the backend and auth.
Note: Running the app locally involves multiple services and configuring auth. For easier testing, please use the deployed version.
- TypeScript – End-to-end types
- Next.js – App Router with React 19
- TailwindCSS + shadcn/ui – Fast, accessible UI
- Convex – Real-time data + auth via
@convex-dev/auth - Turborepo – Monorepo orchestration
- Biome – Linting/formatting
- Monorepo layout:
apps/web(Next.js UI),packages/backend(Convex functions, schema, auth). Shared types come from Convex codegen and are consumed in the app via the workspace dependency@smart-polling/backend. - Auth:
@convex-dev/authwithPasswordandAnonymousproviders. Seepackages/backend/convex/auth.ts. New password sign-ins can upgrade an existing anonymous user by patching the same record. - Data model: Defined in
packages/backend/convex/schema.ts.pollholds metadata (visibility, anonymous allowed, start/end times, creator)pollQuestionholds per-question config with optionalreliesOndependencypollResponsestores user answers
- Conditional logic model:
pollQuestion.reliesOnreferences another question by id and stores a simpleconditionstring ("equals:value" or "contains:value"). This keeps the DB schema minimal and makes evaluation deterministic on the client. - Conditional logic evaluation: Performed client-side in
apps/web/src/app/polls/[id]/page.tsxbyisQuestionVisible. It checks dependency chains, resolves the operator, and supports both single/multi-choice and text questions. - Scheduling: Start/end timestamps are enforced in mutations (see
submitResponse) and echoed to the client for display and disabling inputs.
- Install deps
bun install- Configure Convex (local)
# Creates/links a Convex project and runs until the dev server is reachable
bun dev:setupFollow the prompts to create a new Convex project and connect it to your application.
- Generate auth keys (required by
@convex-dev/auth)
The backend includes a helper that prints a JWT_PRIVATE_KEY and JWKS JSON to stdout. Run it and copy the output into your Convex env.
# Prints two lines: JWT_PRIVATE_KEY=... and JWKS=...
bun generate-keysSet these in your Convex dev environment
-
Copy the
.env.localfrompackages/backendtoapps/web/.envfollowing the example inapps/web/.env.example -
Then run the app
bun devNow visit http://localhost:3000.
- Authoring (
apps/web/src/app/polls/create/page.tsx):- Each question can optionally depend on a prior question via
reliesOn, selecting either:equals:<option>(for choice questions)contains:<text>(for text questions)
- The UI constrains valid dependencies to earlier questions and chooses a sensible default operator based on the source question type.
- Each question can optionally depend on a prior question via
- Persistence (
packages/backend/convex/polls.ts):- On creation, the UI maps an author-time
questionIndexinto the storedreliesOn.questionIdby inserting questions in order and resolving ids.
- On creation, the UI maps an author-time
- Evaluation (
apps/web/src/app/polls/[id]/page.tsx):isQuestionVisiblechecks dependency chains recursively, parses theconditionstring, and returns visibility. Choice answers supportequals; text answers support case-insensitivecontains.
bun dev: Start all apps (Turborepo)bun build: Build all appsbun dev:web: Start the web app onlybun dev:server: Start the Convex backend onlybun dev:setup: Configure Convex and run until readybun check-types: Type-check all appsbun check: Biome lint/formatbun generate-keys: Generates the keys for the auth to work
- Server-side validation for conditional visibility: mirror client logic server-side to validate dependencies on write and on response submission.
- Results and analytics: aggregate views, per-question breakdowns, and filters by answer.
- Access control: per-poll roles (owner, collaborators), link-based private access tokens.
- Rate limiting and abuse prevention: IP/device-based heuristics for anonymous responses.
- Stronger condition language: AND/OR groups, numeric ops, regex, and multi-dependency graphs with guardrails.
- Optimistic UI and offline: optimistic submits with reconcile on reconnect.
- E2E tests: Playwright for flows (create, depend, participate, close).