Architecture

Technical architecture of QuickVote

← Back to Documentation

System Overview

QuickVote 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.

Admin Browser (Dashboard) Participant (Mobile Browser) Supabase Realtime (WebSocket) Supabase PostgreSQL (Database) Realtime (Broadcast) Anonymous Auth

Data Model

Core Entities

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

Entity Relationships

Application Structure

src/
├── 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

Key Workflows

Session Lifecycle

draft ──► lobby ──► active ──► ended │ │ │ │ │ └── Results viewable, no more votes │ └── Participants can join, see lobby └── Admin-only, editing questions

Question Flow

  1. Admin creates questions (draft status)
  2. Admin activates a question → status = active
  3. Participants see the question and vote
  4. Votes stream in via Supabase Realtime
  5. Admin closes question → status = closed
  6. Results display with bar chart and reasons

Batch Voting Flow

  1. Admin groups questions into a batch
  2. Admin activates batch → all batch questions become active
  3. Participants swipe through carousel, selecting answers
  4. On "Submit All", votes upsert in parallel
  5. Admin closes batch → all questions become closed

Real-time Architecture

Communication Patterns

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

State Synchronization

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

State Management

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;
}

Security Model

Row Level Security (RLS)

Authentication

Environment Variables

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