Technical architecture of QuickVote
← Back to DocumentationQuickVote is a client-side React application backed by Supabase for database, authentication, and real-time synchronization. The admin controls session flow from a dashboard while participants vote on their mobile devices. Sessions support team-based voting with up to five named teams, and a visual template editor lets admins design session templates with drag-and-drop batch and question layout.
| Entity | Key Fields | Description |
|---|---|---|
sessions |
id, session_id, admin_token, status, teams, test_mode | Voting session container |
batches |
id, session_id, name, position, status, cover_image_path | Groups questions for batch voting |
questions |
id, session_id, batch_id, text, type, status | Individual poll questions |
votes |
id, question_id, participant_id, value, reason, team_id | Participant vote records |
team_idsrc/
├── components/ # Reusable UI components
│ ├── BarChart.tsx # Vote result visualization
│ ├── BatchVotingCarousel.tsx # Participant batch voting UI
│ ├── VoteAgreeDisagree.tsx # Agree/Disagree voting buttons
│ ├── VoteMultipleChoice.tsx # Multiple choice voting cards
│ ├── QRCode.tsx # Join QR code generator
│ ├── TeamPicker.tsx # Team selection for participants
│ ├── TeamBadge.tsx # Team name/color badge
│ ├── TeamFilterTabs.tsx # Filter results by team
│ ├── TeamQRGrid.tsx # Per-team QR codes grid
│ ├── editor/ # Template editor components
│ │ ├── BatchEditor.tsx # Batch editing panel
│ │ ├── EditorMainArea.tsx # Central editor canvas
│ │ ├── EditorSidebar.tsx # Sidebar with item list
│ │ └── PreviewMode.tsx # Live template preview
│ └── ...
│
├── pages/ # Route components
│ ├── Home.tsx # Landing page with join form
│ ├── AdminSession.tsx # Main admin dashboard (~1400 LOC)
│ ├── ParticipantSession.tsx # Participant voting view
│ ├── SessionReview.tsx # Post-session results review
│ └── TemplateEditorPage.tsx # Visual template editor
│
├── hooks/ # Custom React hooks
│ ├── use-realtime-channel.ts # Supabase realtime wrapper
│ ├── use-presence.ts # Participant presence tracking
│ ├── use-countdown.ts # Timer hook
│ ├── use-haptic.ts # Mobile haptic feedback
│ └── use-multi-select.ts # Multi-select for sequence items
│
├── stores/ # Zustand state stores
│ ├── session-store.ts # Central session state
│ └── template-editor-store.ts # Template editor working state
│
├── lib/ # Utilities
│ ├── supabase.ts # Supabase client instance
│ ├── session-import.ts # JSON import logic
│ ├── session-export.ts # JSON export logic
│ ├── vote-aggregation.ts # Vote counting utilities
│ ├── team-api.ts # Team config validation
│ ├── chart-colors.ts # Color palettes
│ └── color-contrast.ts # Text color from background
│
└── types/ # TypeScript definitions
└── database.ts # Entity type definitions
activeclosedactiveclosed| Pattern | Direction | Purpose |
|---|---|---|
| Broadcast | Admin → Participants | Commands: question_activated, question_closed, batch_activated, timer_started, team_filter_changed |
| Presence | Bidirectional | Participant count tracking, connection status |
| Postgres Changes | Database → Clients | Live vote updates, question status changes |
Admin Action Broadcast Event Participant Response ───────────────────────────────────────────────────────────────── Activate Question → question_activated → Show voting UI Close Question → question_closed → Show waiting state Start Timer → timer_started → Display countdown Activate Batch → batch_activated → Show carousel Close Batch → batch_closed → Return to lobby Filter by Team → team_filter_changed → Show filtered results
The useSessionStore (Zustand) holds all session state:
interface SessionState {
// Data
session: Session | null;
questions: Question[];
batches: Batch[];
// Voting
currentVote: Vote | null;
questionVotes: Vote[];
submitting: boolean;
// Realtime
participantCount: number;
connectionStatus: ConnectionStatus;
activeQuestionId: string | null;
activeBatchId: string | null;
activeSessionItemId: string | null;
navigationDirection: 'forward' | 'backward' | null;
timerEndTime: number | null;
}
team_id is set at vote timeadmin_token in session recordVITE_ADMIN_PASSWORD env var| Variable | Required | Description |
|---|---|---|
VITE_SUPABASE_URL |
Yes | Supabase project URL |
VITE_SUPABASE_ANON_KEY |
Yes | Supabase anon/public key |
VITE_ADMIN_PASSWORD |
No | Password for admin pages |