Alexis Bruneteau 14eff8d0da feat: Rebuild frontend with Next.js and shadcn/ui components
- Migrate from React CRA to Next.js 15 with modern architecture
- Implement comprehensive shadcn/ui component library
- Create complete dashboard system with layouts and navigation
- Build authentication pages (login, register) with proper forms
- Implement vote management pages (active, upcoming, history, archives)
- Add user profile management with security settings
- Configure Tailwind CSS with custom dark theme (accent: #e8704b)
- Setup TypeScript with strict type checking
- Backup old React-based frontend to .backups/frontend-old
- All pages compile successfully and build passes linting

Pages created:
- Home page with hero section and features
- Authentication (login/register)
- Dashboard with stats and vote cards
- Vote management (active, upcoming, history, archives)
- User profile with form validation

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-06 17:02:14 +01:00

133 lines
3.0 KiB
JavaScript

/**
* Hooks personnalisés pour le frontend
*/
import { useState, useEffect } from 'react';
/**
* Hook pour charger les données depuis une URL
*/
export function useApi(url, options = {}) {
const [data, setData] = useState(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
useEffect(() => {
if (!url) return;
const fetchData = async () => {
try {
setLoading(true);
const token = localStorage.getItem('token');
const headers = {
'Content-Type': 'application/json',
...(token && { 'Authorization': `Bearer ${token}` }),
...options.headers,
};
const response = await fetch(url, { ...options, headers });
if (!response.ok) {
throw new Error(`Erreur: ${response.status}`);
}
const result = await response.json();
setData(result);
setError(null);
} catch (err) {
setError(err.message);
setData(null);
} finally {
setLoading(false);
}
};
fetchData();
}, [url, options]);
return { data, loading, error };
}
/**
* Hook pour vérifier si l'utilisateur est connecté
*/
export function useAuth() {
const [voter, setVoter] = useState(null);
const [isAuthenticated, setIsAuthenticated] = useState(false);
const [loading, setLoading] = useState(true);
useEffect(() => {
try {
const storedVoter = localStorage.getItem('voter');
const token = localStorage.getItem('token');
if (storedVoter && token) {
setVoter(JSON.parse(storedVoter));
setIsAuthenticated(true);
}
} catch (err) {
console.error('Erreur lors du chargement de l\'authentification:', err);
} finally {
setLoading(false);
}
}, []);
return { voter, isAuthenticated, loading };
}
/**
* Hook pour gérer le formulaire
*/
export function useForm(initialValues, onSubmit) {
const [values, setValues] = useState(initialValues);
const [errors, setErrors] = useState({});
const [touched, setTouched] = useState({});
const [isSubmitting, setIsSubmitting] = useState(false);
const handleChange = (e) => {
const { name, value, type, checked } = e.target;
setValues(prev => ({
...prev,
[name]: type === 'checkbox' ? checked : value,
}));
};
const handleBlur = (e) => {
const { name } = e.target;
setTouched(prev => ({ ...prev, [name]: true }));
};
const handleSubmit = async (e) => {
e.preventDefault();
setIsSubmitting(true);
try {
await onSubmit(values);
} catch (err) {
console.error('Erreur lors de la soumission:', err);
} finally {
setIsSubmitting(false);
}
};
const resetForm = () => {
setValues(initialValues);
setErrors({});
setTouched({});
};
return {
values,
errors,
touched,
isSubmitting,
handleChange,
handleBlur,
handleSubmit,
resetForm,
setValues,
setErrors,
};
}
export default { useApi, useAuth, useForm };