12 KiB
E-Voting Frontend - Next.js + ShadCN/UI Guide
Overview
The E-Voting frontend has been completely rebuilt using Next.js 15 and shadcn/ui components. This provides a modern, type-safe, and fully responsive user interface for the e-voting platform.
Key Technologies
- Framework: Next.js 15 with App Router
- UI Components: shadcn/ui (Radix UI + Tailwind CSS)
- Styling: Tailwind CSS with custom dark theme
- Language: TypeScript with strict type checking
- Icons: Lucide React
- Forms: React Hook Form (ready for integration)
Project Structure
frontend/
├── app/ # Next.js App Router pages
│ ├── layout.tsx # Root layout with metadata
│ ├── page.tsx # Home page (landing)
│ ├── globals.css # Global styles and CSS variables
│ ├── auth/
│ │ ├── login/page.tsx # Login page
│ │ └── register/page.tsx # Registration page
│ └── dashboard/
│ ├── layout.tsx # Dashboard layout with sidebar
│ ├── page.tsx # Dashboard home
│ ├── profile/page.tsx # User profile management
│ └── votes/
│ ├── active/page.tsx # Active votes
│ ├── upcoming/page.tsx # Upcoming votes
│ ├── history/page.tsx # Vote history
│ └── archives/page.tsx # Archived votes
├── components/
│ └── ui/ # Reusable UI components
│ ├── button.tsx # Button component with variants
│ ├── card.tsx # Card component with subcomponents
│ ├── input.tsx # Input field component
│ ├── label.tsx # Label component
│ └── index.ts # Component exports
├── lib/
│ └── utils.ts # Utility functions (cn helper)
├── public/ # Static assets
├── styles/ # Additional stylesheets
├── package.json # Dependencies and scripts
├── tsconfig.json # TypeScript configuration
├── tailwind.config.ts # Tailwind CSS configuration
├── next.config.js # Next.js configuration
└── postcss.config.js # PostCSS configuration
Running the Project
Development
cd frontend
npm install
npm run dev
The application will be available at http://localhost:3000.
Production Build
npm run build
npm start
Linting
npm run lint
Pages Overview
Public Pages
1. Home Page (/)
- Hero section with call-to-action
- Stats section (1000+ voters, 50+ elections, 99.9% security)
- Features section highlighting key benefits
- Navigation to login/register
- Responsive design for mobile
2. Login Page (/auth/login)
- Email and password input fields
- Error display with icons
- Loading state during submission
- Link to registration page
- Feature highlights illustration
3. Register Page (/auth/register)
- First name, last name, email, password fields
- Password confirmation validation
- Success/error state handling
- Feature highlights on form side
Protected Pages (Dashboard)
4. Dashboard Home (/dashboard)
- Welcome section with user name
- Stats cards (active votes, upcoming, past, archives)
- Active votes carousel
- Quick action buttons
- Responsive grid layout
5. Active Votes (/dashboard/votes/active)
- List of ongoing elections
- Progress bars showing participation
- Vote count and candidate information
- Filter by category (National, Local, Regional)
- "Participate" button for each vote
6. Upcoming Votes (/dashboard/votes/upcoming)
- Timeline view of future elections
- Importance indicators (color-coded)
- Start date and time for each vote
- "Notify me" button for reminders
- Category and importance filtering
7. Vote History (/dashboard/votes/history)
- Past elections with results
- Participation indicator (checkmark if voted)
- Stats: total voted, participation rate
- Results preview (winner and participation %)
- Filterable by participation status
8. Archives (/dashboard/votes/archives)
- Historical elections organized by year
- Document count per election
- Download and consult options
- Year filtering
- Grid layout for browsing
9. Profile Page (/dashboard/profile)
- Personal information form
- Address and contact details
- Password change section
- Two-factor authentication status
- Session management
- Account deletion option
Design System
Color Palette
The custom dark theme uses CSS variables defined in app/globals.css:
--background: 23 23 23 (rgb(23, 23, 23))
--foreground: 224 224 224 (rgb(224, 224, 224))
--primary: 232 112 75 (rgb(232, 112, 75)) [Accent]
--secondary: 163 163 163
--muted: 115 115 115
--border: 82 82 82
--input: 82 82 82
--card: 39 39 39
Component Patterns
Button Component
import { Button } from "@/components/ui/button"
// Default variant
<Button>Submit</Button>
// Outline variant
<Button variant="outline">Cancel</Button>
// Destructive variant
<Button variant="destructive">Delete</Button>
// Ghost variant (no background)
<Button variant="ghost">Link</Button>
// Sizes
<Button size="sm">Small</Button>
<Button size="lg">Large</Button>
Card Component
import { Card, CardHeader, CardTitle, CardDescription, CardContent } from "@/components/ui/card"
<Card>
<CardHeader>
<CardTitle>Title</CardTitle>
<CardDescription>Description</CardDescription>
</CardHeader>
<CardContent>
{/* Content */}
</CardContent>
</Card>
Input Component
import { Input } from "@/components/ui/input"
<Input
type="email"
placeholder="Enter email"
value={value}
onChange={handleChange}
/>
Label Component
import { Label } from "@/components/ui/label"
<Label htmlFor="email">Email Address</Label>
<Input id="email" type="email" />
State Management
Currently, all state is managed with React hooks (useState). For more complex state management, consider:
- Context API for global state (authentication, user preferences)
- TanStack Query for server state (API calls, caching)
- Zustand for client state (if scaling up)
Styling Guide
Using Tailwind Classes
All styling uses Tailwind CSS utility classes. Custom CSS is avoided.
<div className="flex items-center justify-between p-4 rounded-lg bg-card border border-border hover:border-accent transition-colors">
<span className="text-sm font-medium text-foreground">Label</span>
<button className="text-accent hover:text-accent/80 transition-colors">Action</button>
</div>
Responsive Design
Use Tailwind's responsive prefixes:
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-4">
{/* Responsive grid */}
</div>
Adding New Pages
Create a new page
- Create a new file in
app/[section]/[page]/page.tsx - Make it a "use client" component if it needs interactivity
- Use existing components from
components/ui/ - Follow the naming conventions and styling patterns
Example:
"use client"
import { Button } from "@/components/ui/button"
import { Card, CardHeader, CardTitle } from "@/components/ui/card"
export default function NewPage() {
return (
<div className="space-y-8">
<h1 className="text-3xl font-bold">New Page Title</h1>
<Card>
<CardHeader>
<CardTitle>Card Title</CardTitle>
</CardHeader>
</Card>
<Button>Action</Button>
</div>
)
}
Adding New Components
Create a new UI component
- Create
components/ui/component-name.tsx - Export from
components/ui/index.ts - Use Radix UI primitives as base if available
- Style with Tailwind CSS
- Include proper TypeScript types
Example:
import React from "react"
export interface CustomButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {
variant?: "default" | "outline"
size?: "sm" | "md" | "lg"
}
export const CustomButton = React.forwardRef<HTMLButtonElement, CustomButtonProps>(
({ className, variant = "default", size = "md", ...props }, ref) => {
return (
<button
ref={ref}
className={`px-4 py-2 rounded-lg font-medium transition-colors ${
variant === "outline" ? "border border-border hover:bg-muted" : "bg-accent text-white hover:bg-accent/90"
}`}
{...props}
/>
)
}
)
CustomButton.displayName = "CustomButton"
Integration with Backend
The frontend is ready to integrate with the E-Voting backend API. Currently, API calls are commented out or return mock data.
API Endpoints to Connect
Authentication
POST /api/auth/register- User registrationPOST /api/auth/login- User loginPOST /api/auth/logout- User logoutPOST /api/auth/refresh- Refresh authentication token
Votes
GET /api/votes/active- Get active votesGET /api/votes/upcoming- Get upcoming votesGET /api/votes/:id- Get vote detailsPOST /api/votes/:id/participate- Submit voteGET /api/votes/history- Get vote historyGET /api/votes/archives- Get archived votes
User
GET /api/user/profile- Get user profilePUT /api/user/profile- Update profilePUT /api/user/password- Change passwordGET /api/user/sessions- Get active sessions
Example API Integration
const [data, setData] = useState(null)
const [loading, setLoading] = useState(false)
const [error, setError] = useState(null)
useEffect(() => {
const fetchData = async () => {
setLoading(true)
try {
const response = await fetch("/api/votes/active")
const result = await response.json()
setData(result)
} catch (err) {
setError(err)
} finally {
setLoading(false)
}
}
fetchData()
}, [])
Accessibility (a11y)
All components follow WCAG 2.1 guidelines:
- Proper heading hierarchy
- ARIA labels on form inputs
- Keyboard navigation support
- Color contrast ratios > 4.5:1
- Focus indicators visible
Performance Optimization
- Code Splitting: Next.js automatically splits code at route boundaries
- Image Optimization: Use
next/imagefor optimized images - Font Optimization: System fonts used by default (fast loading)
- CSS-in-JS: Tailwind generates minimal CSS bundle
Current build size: ~117 kB First Load JS (shared by all pages)
Troubleshooting
Common Issues
Build fails with TypeScript errors:
npm run build -- --no-lint
Dependencies conflict:
npm install --legacy-peer-deps
Cache issues:
rm -rf .next node_modules
npm install
npm run build
Next Steps
- API Integration: Connect authentication and vote endpoints
- State Management: Implement user session management with Context
- Error Handling: Add error boundaries and error pages
- Loading States: Show skeleton screens during data fetching
- Validation: Implement form validation with Zod + React Hook Form
- Testing: Add unit tests with Jest and E2E tests with Cypress
- Analytics: Integrate analytics tracking
- PWA: Add PWA capabilities for offline support
Resources
- Next.js Documentation
- shadcn/ui Documentation
- Tailwind CSS Documentation
- Radix UI Documentation
- TypeScript Documentation
Support
For questions or issues related to the frontend, refer to:
- Commit history:
git log --oneline - Recent changes:
git diff main..UI - Build output: Check terminal after
npm run build