A web-based e-voting platform built for student elections at schools, colleges and universities. The goal was simple: replace paper ballots and long queues with something voters can use on a phone in under a minute, while giving election officers a live view of what's happening on the ground.
It's a multi-tenant system — one deployment can host several institutions, each with its own voters, candidates, halls, programmes and elections.
- Voter flow — voters authenticate with their student ID and a one-time SMS code, pick candidates position-by-position, confirm their ballot, and get a thank-you screen. One vote per voter, per election. "Decline to vote" is supported for every position.
- Admin dashboard — create elections, manage candidates, positions, programmes, halls and year levels, import voters from CSV/Excel, and monitor turnout as it happens.
- Polling officer view — a slimmer dashboard for staff running a polling station: live turnout counts, per-hall breakdowns, and voter lookup.
- Results — tallies are computed server-side and only released when an election is closed. Public results pages are generated per school/election.
- Role-based access — super admin, school admin, polling officer and custom roles with per-resource permissions.
- Audit log — every admin action is recorded with actor, target and timestamp.
- Frontend: Vite, React 18, TypeScript, Tailwind CSS, shadcn/ui, React Router, TanStack Query, React Hook Form + Zod
- Backend: Supabase (Postgres, Auth, Storage, Row-Level Security, Edge Functions written in Deno/TypeScript)
- SMS / OTP: Arkesel (Ghana SMS gateway)
- Spreadsheet import:
xlsx+papaparsefor CSV/Excel voter rolls
src/
pages/
admin/ # Admin dashboard, voters, candidates, results, settings, etc.
voter/ # Auth, candidate selection, confirm, thank-you, results
components/
admin/ # Dialogs, tables, filters for admin pages
voter/ # Cards, ballot sections, voting progress
layouts/ # Admin layout, sidebar, header
security/ # Security providers & guards
ui/ # shadcn/ui primitives
contexts/ # Election, School, Permission, Settings providers
hooks/ # Data + performance hooks
integrations/
supabase/ # Typed Supabase client
services/ # Preload & caching services
supabase/
functions/ # Deno edge functions (auth, OTP, votes, admin, SMS)
migrations/ # SQL migrations — schema, RLS policies, RPCs
You'll need Node.js 18+ and either npm or Bun (a bun.lockb
is checked in, but npm install works fine).
git clone https://github.com/<your-username>/<repo-name>.git
cd <repo-name>
npm install
cp .env.example .env
# fill in your Supabase values in .env
npm run devThe dev server runs on http://localhost:8080 (see vite.config.ts).
Copy .env.example to .env and fill in:
| Variable | Where it's used | Required |
|---|---|---|
VITE_SUPABASE_URL |
Frontend Supabase client | ✅ |
VITE_SUPABASE_PUBLISHABLE_KEY |
Frontend Supabase client (anon key) | ✅ |
VITE_SUPABASE_PROJECT_ID |
Optional — reserved for tooling | — |
Edge-function secrets (SUPABASE_SERVICE_ROLE_KEY, ARKESEL_API_KEY,
ARKESEL_SMS_API_KEY, ARKESEL_SENDER_ID) are not read by the frontend.
Set them on the Supabase side:
supabase secrets set ARKESEL_API_KEY=... ARKESEL_SENDER_ID=E-Voting- Install the Supabase CLI.
- Create a project in the Supabase dashboard and copy the URL + anon key into
your
.env. - Update
supabase/config.tomlwith yourproject_id(the ref from the dashboard URL). - Link and push the schema:
supabase link --project-ref <your-project-ref> supabase db push supabase functions deploy
| Command | What it does |
|---|---|
npm run dev |
Start Vite dev server |
npm run build |
Production build |
npm run build:dev |
Development-mode build (source maps, no minify) |
npm run preview |
Preview the production build locally |
npm run lint |
Run ESLint |
- Real secrets (service-role key, SMS API keys) live in Supabase Edge Function secrets, not in the frontend bundle.
- Row-Level Security is enabled on every table; anon key access is restricted to what the voter flow actually needs.
- Sensitive operations (creating admins, deleting voters, submitting votes, computing results) go through dedicated edge functions that verify the caller server-side.
- Never commit a real
.envfile..env,.env.localand.env.productionare all gitignored — use.env.exampleas your template.
This project was originally built for a specific institution and then generalised. It's feature-complete for the elections it was designed for; if you want to adapt it to a different org, expect to tweak the schools/programmes model and the SMS provider.
No license file is included. Treat the code as "all rights reserved" unless a
LICENSE is added later. Open an issue if you'd like to use it — happy to
discuss.