TanStack Start + React client application for the RemoteVote NG electronic voting platform. A full-featured electoral management interface serving voters, presiding officers, commissioners, and the INEC Secretary β built with a premium dark-mode design system.
- Overview
- Tech Stack
- Project Structure
- Pages & Routes
- Role-Based Dashboards
- Environment Variables
- Getting Started (Local Development)
- Building for Production
- Deployment (Vercel)
- Key Features
- Design System
The RemoteVote NG frontend is a sophisticated single-page application serving the full e-voting lifecycle:
- Voter Portal β NIN-authenticated registration, OTP verification, and secure ballot casting
- Commissioner Dashboard β Election creation, candidate management, and staff onboarding
- Secretary Dashboard β System-wide metrics, audit logs, and oversight controls
- Field Officer Views β Result sheet submission, accreditation review
- Observer/Agent Views β Live election monitoring and dispute filing
- Accreditation Portal β Media and observer application submission
| Layer | Technology |
|---|---|
| Framework | TanStack Start (SSR + file-based routing) |
| UI Library | React 19 |
| Language | TypeScript 5 |
| Styling | Tailwind CSS v4 |
| Component Primitives | Radix UI |
| Icons | Lucide React |
| Charts | Recharts |
| Forms | React Hook Form + Zod |
| Routing | TanStack Router (type-safe, file-based) |
| State / Data | TanStack Query |
| Build Tool | Vite 8 |
| Deployment | Vercel (Cloudflare Workers / Nitro preset) |
frontend/
βββ public/ # Static assets
βββ src/
β βββ components/
β β βββ ui/ # Radix UI component library (shadcn-style)
β βββ lib/
β β βββ api.ts # API client (reads VITE_API_BASE_URL from .env)
β βββ routes/
β β βββ __root.tsx # Root layout with auth context
β β βββ index.tsx # Landing page / hero
β β βββ signup.tsx # Voter registration
β β βββ onboard.tsx # Staff onboarding (token-based invite accept)
β β βββ accreditation.tsx # Observer/media accreditation portal
β β βββ dashboard.tsx # Role-dispatching dashboard (all roles)
β β βββ vote.$id.tsx # Ballot casting page (dynamic election ID)
β β βββ results.tsx # Public election results viewer
β βββ server.ts # SSR error wrapper
β βββ styles.css # Global design system tokens
βββ .env # Local environment variables
βββ vercel.json # Vercel SPA routing configuration
βββ vite.config.ts # Vite + TanStack config
βββ tsconfig.json # TypeScript configuration
βββ package.json
| Route | Description | Auth Required |
|---|---|---|
/ |
Landing page with login form | No |
/signup |
Voter NIN registration + NIMC verification | No |
/onboard |
Staff account activation via invitation token | No |
/accreditation |
Observer/media accreditation application | No |
/dashboard |
Role-specific control dashboard | β Yes |
/vote/:id |
Ballot casting for a specific election | β Yes (voter) |
/results |
Public election results and candidate stats | No |
The /dashboard route renders a different experience based on the authenticated user's role:
- Live list of active elections
- One-click ballot casting navigation
- Participation history with cryptographic receipts
Tab 1 β Elections
- Create, publish, advance, and delete elections
- Add/remove candidates with party colours and manifestos
- Multi-signature election closure approval
Tab 2 β Accreditations
- Review and approve/reject observer/media applications
Tab 3 β Staff Invitations
- Generate secure onboarding invitations
- Bulk invite via CSV upload
- Export invitation logs to CSV
Tab 4 β Polling Units
- Full CRUD for polling unit registry
- Import polling units from CSV
- Export to CSV
Tab 5 β NIMC Records
- Full CRUD for the simulated NIMC identity database
- Import and export CSV
All commissioner capabilities, plus:
Tab 1 β Metrics
- System-wide stats: total voters, votes cast, polling units, elections, staff
- Live metric cards
Tab 7 β System Audit Logs
- Full audit trail of all CRUD operations
- Expandable JSON detail view per action
- Assigned polling unit overview
- Form EC8A (ResultSheet) submission
- Dispute filing
- Read-only election status view
- Live dispute submission
Create a .env file in the frontend/ directory:
# Base URL of the Django REST API backend
# Development default: http://localhost:8000/api
# Production: replace with your deployed backend URL
VITE_API_BASE_URL=http://localhost:8000/apiImportant: All frontend environment variables must be prefixed with
VITE_to be exposed to client-side code by Vite.
When deploying to Vercel, add VITE_API_BASE_URL in the Vercel Environment Variables dashboard.
- Node.js 18+
- npm 9+
# 1. Navigate to the frontend directory
cd frontend
# 2. Install dependencies
npm install
# 3. Create your .env file
echo "VITE_API_BASE_URL=http://localhost:8000/api" > .env
# 4. Start the development server
npm run devThe app will be available at http://localhost:3000 (or the next available port).
Make sure the Django backend is also running at
http://localhost:8000before testing authenticated routes.
npm run buildThis runs a full Vite + Nitro production build outputting to .output/:
.output/public/β Static client assets.output/server/β SSR server bundle (Nitro / Cloudflare Workers format)
The frontend is configured for Vercel deployment via vercel.json, which rewrites all paths to index.html to support client-side routing.
- Push the
frontend/folder to a GitHub repository (or as a monorepo subdirectory) - Connect the repository to Vercel
- Set the Root Directory to
frontend - Add the environment variable:
VITE_API_BASE_URL = https://your-backend.vercel.app/api - Deploy
- NIN-based login for voters
- Staff Number login for electoral officials
- OTP-based multi-factor authentication via email (Brevo)
- Persistent session via localStorage token
Available for Commissioners and Secretaries on Polling Units, NIMC Records, and Staff Invitations:
Export CSV β Downloads a properly formatted .csv file of the currently filtered/visible records.
Import CSV β Upload a .csv file to bulk-create records. Column headers are mapped fuzzily, so minor variations (e.g. "Name" vs "Facility Name") are handled automatically.
Template formats:
Polling Units CSV:
Code / ID,Name,Ward,LGA,State,Registered Voters
,Alausa Primary School,Ward 1,Ikeja,Lagos,1250NIMC Records CSV:
NIN,Full Name,State,LGA,Biometric Hash
12345678901,Adaeze Nwosu,Lagos,Ikeja,mock_hash_xyzStaff Invitations CSV:
Email,Role
po@example.com,po
co@example.com,coEvery data table supports:
- Full-text search across key fields
- Status-based dropdown filtering
All state and LGA selectors are pre-populated with all 36 Nigerian states + FCT and their respective LGAs.
The UI uses a custom dark-mode-first design system built with Tailwind CSS v4 tokens:
- Font: Inter + DM Sans (via Google Fonts)
- Primary Colour: Deep indigo-brand palette (
--color-brand) - Theme: Rich dark mode with glass-morphism card styles
- Animations: CSS micro-animations on hover states and page transitions
- Components: Fully accessible Radix UI primitives styled with Tailwind
MIT Β© RemoteVote NG β Studio3 Launchpad 2026