VOTING_SYSTEM.md
Especificación técnica desde la carpeta docs del proyecto
Voting System - Complete Guide for AI Assistants
Plataforma Astral - Centro Consejo Voting Platform
Status: Production Ready ✅Last Updated: January 2025
---
🎯 System Overview
The Voting System is a comprehensive platform for democratic decision-making in the Parent Center. It allows administrators to create, manage, and monitor votes, while parents participate securely and transparently.
Key Features
For Administrators:
For Parents:
---
🏗️ Architecture
Database Schema (Convex)
`typescript
// Convex schema models
votes: {
_id: Id<"votes">
title: string
description?: string
category: VoteCategory
endDate: number // Unix timestamp
isActive: boolean
isPublic: boolean
allowMultipleVotes: boolean
maxVotesPerUser?: number
requireAuthentication: boolean
createdBy: Id<"users">
createdAt: number
updatedAt: number
}
voteOptions: {
_id: Id<"voteOptions">
voteId: Id<"votes">
text: string
createdAt: number
}
voteResponses: {
_id: Id<"voteResponses">
voteId: Id<"votes">
optionId: Id<"voteOptions">
userId: Id<"users">
createdAt: number
}`
Convex Functions (convex/votes.ts)
Key Functions:
createVote - Create new vote with optionsgetVotes - List votes with optional filtersgetVoteById - Get vote with options and vote countscastVote - Record a vote response (enforces constraints)deleteVote - Delete vote and all related datagetUserVoteResponse - Check if user has votedgetVoteResults - Calculate vote statisticsAPI Routes
Admin Routes (src/app/api/admin/votes/):
GET /api/admin/votes - List all votes (query: ?isActive=true&category=GENERAL)POST /api/admin/votes - Create new voteDELETE /api/admin/votes/[id] - Delete voteParent Routes (src/app/api/parent/votes/):
GET /api/parent/votes - List public active votes with user voting statusPOST /api/parent/votes - Cast a vote (body: { voteId, optionId })Critical Implementation Pattern:
`typescript
// ✅ CORRECT: Initialize Convex client per-request
export async function GET(request: NextRequest) {
const convex = getConvexClient(); // Per-request initialization
// ... handler logic
}
export const runtime = "nodejs"; // Required for API routes`
Security & Authorization
---
🔒 Voting Constraints
Constraint Enforcement
allowMultipleVotes: false)- First vote succeeds
- Second vote returns
409 Conflict: "You have already voted in this poll"allowMultipleVotes: true)- User can vote multiple times
- Combined with
maxVotesPerUser if setmaxVotesPerUser: number)- Enforces maximum votes per user
- Returns
409 Conflict: "Maximum votes per user limit reached (X)"endDate)- Checked at API layer (not Convex)
- Expired votes show status:
"closed"- Voting on expired vote returns
403 ForbiddenisPublic: boolean)- Private votes hidden from
GET /api/parent/votes- Attempting to vote on private vote returns
403 ForbiddenisActive: boolean)- Inactive votes hidden from
GET /api/parent/votes- Attempting to vote on inactive vote returns
403 ForbiddenError Handling
HTTP Status Codes:
401 Unauthorized - No authentication403 Forbidden - Wrong role or forbidden vote (expired, inactive, private)404 Not Found - Invalid vote/option IDs409 Conflict - Already voted or max votes reached400 Bad Request - Missing/invalid fields---
📊 Vote Categories
`typescript
enum VoteCategory {
GENERAL = "GENERAL", // Gray
ACADEMIC = "ACADEMIC", // Blue
ADMINISTRATIVE = "ADMINISTRATIVE", // Purple
SOCIAL = "SOCIAL", // Green
FINANCIAL = "FINANCIAL", // Yellow
INFRASTRUCTURE = "INFRASTRUCTURE", // Orange
CURRICULUM = "CURRICULUM", // Indigo
EVENTS = "EVENTS", // Pink
POLICIES = "POLICIES", // Red
OTHER = "OTHER", // Gray
}`
---
🔧 Development Patterns
Creating a Vote (Admin)
`typescript
// API Request
POST /api/admin/votes
{
"title": "New School Policy",
"description": "Description here",
"category": "POLICIES",
"endDate": 1735689600000, // Unix timestamp
"isActive": true,
"isPublic": true,
"allowMultipleVotes": false,
"requireAuthentication": true,
"options": [
{ "text": "Option 1" },
{ "text": "Option 2" }
]
}
// Response
{
"success": true,
"data": { "id": "vote-id-123" }
}`
Casting a Vote (Parent)
`typescript
// API Request
POST /api/parent/votes
{
"voteId": "vote-id-123",
"optionId": "option-id-456"
}
// Response
{
"success": true,
"message": "Vote cast successfully",
"data": { "id": "response-id-789" }
}`
Querying Votes
`typescript
// Get all active votes for admin
GET /api/admin/votes?isActive=true
// Get votes by category
GET /api/admin/votes?category=FINANCIAL
// Get public active votes for parent
GET /api/parent/votes
// Returns votes with user voting status included`
---
🧪 Testing
Test Scripts
Convex Function Tests:
`bash
npx tsx scripts/test-voting-system.ts`
Tests:
API Endpoint Tests:
`bash
Requires dev server running
npm run dev
In another terminal
npx tsx scripts/test-voting-api-comprehensive.ts
`Tests:
Test Coverage Checklist
---
🐛 Common Issues & Solutions
Issue: "Module not found: @/convex/\_generated/api"
Solution: Initialize Convex client per-request, not at module level:
`typescript
// ❌ WRONG
const convex = getConvexClient(); // At module level
// ✅ CORRECT
export async function GET(request: NextRequest) {
const convex = getConvexClient(); // Inside handler
}`
Also add: export const runtime = "nodejs";
Issue: Type errors with Convex Id types
Solution: Import and cast properly:
`typescript
import type { Id } from "@/convex/_generated/dataModel";
await convex.mutation(api.votes.deleteVote, {
id: voteId as Id<"votes">,
});`
Issue: Vote constraints not enforced
Solution: Constraints are enforced at both layers:
castVote function checks allowMultipleVotes and maxVotesPerUserisPublic, isActiveIssue: 500 errors on API routes
Solution:
NEXT_PUBLIC_CONVEX_URL is setnpx convex deployexport const runtime = "nodejs";---
📁 File Locations
Backend:
convex/votes.ts - Convex functionsconvex/schema.ts - Database schemaAPI Routes:
src/app/api/admin/votes/route.ts - Admin endpointssrc/app/api/admin/votes/[id]/route.ts - Admin vote operationssrc/app/api/parent/votes/route.ts - Parent endpointsFrontend Components:
src/components/admin/voting/ - Admin voting UIsrc/components/parent/voting/ - Parent voting UIUtilities:
src/lib/voting-utils.ts - Vote calculation helpers---
🚀 Deployment
Prerequisites
`bash
Environment variables required
NEXT_PUBLIC_CONVEX_URL=https://your-project.convex.cloud
CLERK_SECRET_KEY=...
NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY=...
`Deployment Steps
`bash
Deploy Convex backend
npx convex deploy
Deploy Next.js frontend
vercel --prod
`Verification
POST /api/admin/votesGET /api/parent/votesPOST /api/parent/votes---
📚 Related Documentation
docs/AI_KNOWLEDGE_BASE.md - PRIMARY: Complete system documentationdocs/AI_KNOWLEDGE_BASE.md (Clerk + Convex auth system)docs/TESTING_GUIDE.mddocs/ENVIRONMENT.md---
🎯 Key Takeaways for AI Assistants
Id<"votes"> casting for Convex operations---
Maintained by: Development Team
For questions: Refer to API documentation or troubleshooting guides
Archivo: VOTING_SYSTEM.md