CIA/e-voting-system/frontend/lib/auth-context.tsx
Alexis Bruneteau f825a2392c feat: Implement dark theme for frontend with toggle
Changes:
- Add next-themes dependency for theme management
- Create ThemeProvider wrapper for app root layout
- Set dark mode as default theme
- Create ThemeToggle component with Sun/Moon icons
- Add theme toggle to home page navigation
- Add theme toggle to dashboard header
- App now starts in dark mode with ability to switch to light mode

Styling uses existing Tailwind dark mode variables configured in
tailwind.config.ts and globals.css. All existing components automatically
support dark theme.

🤖 Generated with Claude Code

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-07 16:35:44 +01:00

152 lines
3.9 KiB
TypeScript

/**
* Authentication Context
* Manages user authentication state globally
*/
"use client"
import React, { createContext, useContext, useState, useEffect, ReactNode } from "react"
import { authApi, getAuthToken, setAuthToken, clearAuthToken, VoterProfile } from "./api"
interface AuthContextType {
user: VoterProfile | null
isLoading: boolean
isAuthenticated: boolean
error: string | null
login: (email: string, password: string) => Promise<void>
register: (email: string, password: string, firstName: string, lastName: string, citizenId: string) => Promise<void>
logout: () => void
refreshProfile: () => Promise<void>
}
const AuthContext = createContext<AuthContextType | undefined>(undefined)
export function AuthProvider({ children }: { children: ReactNode }) {
const [user, setUser] = useState<VoterProfile | null>(null)
const [isLoading, setIsLoading] = useState(true)
const [error, setError] = useState<string | null>(null)
// Check if user is already logged in on mount
useEffect(() => {
const checkAuth = async () => {
const token = getAuthToken()
if (token) {
try {
const response = await authApi.getProfile()
if (response.data) {
setUser(response.data)
} else {
clearAuthToken()
}
} catch (err) {
clearAuthToken()
}
}
setIsLoading(false)
}
checkAuth()
}, [])
const login = async (email: string, password: string) => {
setIsLoading(true)
setError(null)
try {
const response = await authApi.login(email, password)
if (response.error) {
throw new Error(response.error)
}
if (response.data) {
setAuthToken(response.data.access_token)
setUser({
id: response.data.id,
email: response.data.email,
first_name: response.data.first_name,
last_name: response.data.last_name,
has_voted: false,
created_at: new Date().toISOString(),
})
}
} catch (err) {
const message = err instanceof Error ? err.message : "Login failed"
setError(message)
throw err
} finally {
setIsLoading(false)
}
}
const register = async (email: string, password: string, firstName: string, lastName: string, citizenId: string) => {
setIsLoading(true)
setError(null)
try {
const response = await authApi.register(email, password, firstName, lastName, citizenId)
if (response.error) {
throw new Error(response.error)
}
if (response.data) {
setAuthToken(response.data.access_token)
setUser({
id: response.data.id,
email: response.data.email,
first_name: response.data.first_name,
last_name: response.data.last_name,
has_voted: false,
created_at: new Date().toISOString(),
})
}
} catch (err) {
const message = err instanceof Error ? err.message : "Registration failed"
setError(message)
throw err
} finally {
setIsLoading(false)
}
}
const logout = () => {
authApi.logout()
setUser(null)
setError(null)
}
const refreshProfile = async () => {
try {
const response = await authApi.getProfile()
if (response.data) {
setUser(response.data)
} else {
clearAuthToken()
setUser(null)
}
} catch (err) {
clearAuthToken()
setUser(null)
}
}
const value: AuthContextType = {
user,
isLoading,
isAuthenticated: user !== null,
error,
login,
register,
logout,
refreshProfile,
}
return <AuthContext.Provider value={value}>{children}</AuthContext.Provider>
}
/**
* Hook to use authentication context
*/
export function useAuth() {
const context = useContext(AuthContext)
if (context === undefined) {
throw new Error("useAuth must be used within an AuthProvider")
}
return context
}