diff --git a/app/api/adherents/[id]/route.ts b/app/api/adherents/[id]/route.ts new file mode 100644 index 0000000..48de07e --- /dev/null +++ b/app/api/adherents/[id]/route.ts @@ -0,0 +1,103 @@ +import { NextRequest, NextResponse } from 'next/server'; +import { prisma } from '@/lib/prisma'; +import { getCurrentUser } from '@/lib/auth'; + +// GET - Obtenir un adhérent par ID +export async function GET( + request: NextRequest, + { params }: { params: { id: string } } +) { + try { + const user = await getCurrentUser(); + if (!user) { + return NextResponse.json({ error: 'Non autorisé' }, { status: 401 }); + } + + const adherent = await prisma.adherent.findUnique({ + where: { id: params.id }, + }); + + if (!adherent) { + return NextResponse.json( + { error: 'Adhérent non trouvé' }, + { status: 404 } + ); + } + + return NextResponse.json(adherent); + } catch (error) { + console.error('Erreur lors de la récupération de l\'adhérent:', error); + return NextResponse.json( + { error: 'Erreur serveur' }, + { status: 500 } + ); + } +} + +// PUT - Mettre à jour un adhérent +export async function PUT( + request: NextRequest, + { params }: { params: { id: string } } +) { + try { + const user = await getCurrentUser(); + if (!user) { + return NextResponse.json({ error: 'Non autorisé' }, { status: 401 }); + } + + const body = await request.json(); + const { nom, prenom, dateNaissance, adresse, email, telephone, situation, prescripteur, facturation, commentaire, telephoneSecondaire, instructions } = body; + + const updateData: any = {}; + if (nom) updateData.nom = nom; + if (prenom) updateData.prenom = prenom; + if (dateNaissance) updateData.dateNaissance = new Date(dateNaissance); + if (adresse) updateData.adresse = adresse; + if (email) updateData.email = email; + if (telephone) updateData.telephone = telephone; + if (situation !== undefined) updateData.situation = situation || null; + if (prescripteur !== undefined) updateData.prescripteur = prescripteur || null; + if (facturation !== undefined) updateData.facturation = facturation || null; + if (commentaire !== undefined) updateData.commentaire = commentaire || null; + if (telephoneSecondaire !== undefined) updateData.telephoneSecondaire = telephoneSecondaire || null; + if (instructions !== undefined) updateData.instructions = instructions || null; + + const adherent = await prisma.adherent.update({ + where: { id: params.id }, + data: updateData, + }); + + return NextResponse.json(adherent); + } catch (error) { + console.error('Erreur lors de la mise à jour de l\'adhérent:', error); + return NextResponse.json( + { error: 'Erreur serveur' }, + { status: 500 } + ); + } +} + +// DELETE - Supprimer un adhérent +export async function DELETE( + request: NextRequest, + { params }: { params: { id: string } } +) { + try { + const user = await getCurrentUser(); + if (!user) { + return NextResponse.json({ error: 'Non autorisé' }, { status: 401 }); + } + + await prisma.adherent.delete({ + where: { id: params.id }, + }); + + return NextResponse.json({ success: true }); + } catch (error) { + console.error('Erreur lors de la suppression de l\'adhérent:', error); + return NextResponse.json( + { error: 'Erreur serveur' }, + { status: 500 } + ); + } +} diff --git a/app/api/adherents/route.ts b/app/api/adherents/route.ts new file mode 100644 index 0000000..f47aadb --- /dev/null +++ b/app/api/adherents/route.ts @@ -0,0 +1,91 @@ +import { NextRequest, NextResponse } from 'next/server'; +import { prisma } from '@/lib/prisma'; +import { getCurrentUser } from '@/lib/auth'; + +// GET - Liste tous les adhérents avec recherche +export async function GET(request: NextRequest) { + try { + const user = await getCurrentUser(); + if (!user) { + return NextResponse.json({ error: 'Non autorisé' }, { status: 401 }); + } + + const searchParams = request.nextUrl.searchParams; + const search = searchParams.get('search') || ''; + + // Récupérer tous les adhérents (SQLite ne supporte pas bien les recherches complexes) + const allAdherents = await prisma.adherent.findMany({ + orderBy: { createdAt: 'desc' }, + }); + + // Filtrer en JavaScript pour la recherche insensible à la casse + const adherents = search + ? allAdherents.filter( + (adherent) => { + const searchLower = search.toLowerCase(); + return ( + adherent.nom.toLowerCase().includes(searchLower) || + adherent.prenom.toLowerCase().includes(searchLower) || + adherent.email.toLowerCase().includes(searchLower) || + adherent.telephone.includes(search) || + adherent.adresse.toLowerCase().includes(searchLower) || + (adherent.telephoneSecondaire && adherent.telephoneSecondaire.includes(search)) + ); + } + ) + : allAdherents; + + return NextResponse.json(adherents); + } catch (error) { + console.error('Erreur lors de la récupération des adhérents:', error); + return NextResponse.json( + { error: 'Erreur serveur' }, + { status: 500 } + ); + } +} + +// POST - Créer un nouvel adhérent +export async function POST(request: NextRequest) { + try { + const user = await getCurrentUser(); + if (!user) { + return NextResponse.json({ error: 'Non autorisé' }, { status: 401 }); + } + + const body = await request.json(); + const { nom, prenom, dateNaissance, adresse, email, telephone, situation, prescripteur, facturation, commentaire, telephoneSecondaire, instructions } = body; + + if (!nom || !prenom || !dateNaissance || !adresse || !email || !telephone) { + return NextResponse.json( + { error: 'Tous les champs obligatoires sont requis' }, + { status: 400 } + ); + } + + const adherent = await prisma.adherent.create({ + data: { + nom, + prenom, + dateNaissance: new Date(dateNaissance), + adresse, + email, + telephone, + situation: situation || null, + prescripteur: prescripteur || null, + facturation: facturation || null, + commentaire: commentaire || null, + telephoneSecondaire: telephoneSecondaire || null, + instructions: instructions || null, + }, + }); + + return NextResponse.json(adherent, { status: 201 }); + } catch (error) { + console.error('Erreur lors de la création de l\'adhérent:', error); + return NextResponse.json( + { error: 'Erreur serveur' }, + { status: 500 } + ); + } +} diff --git a/app/dashboard/adherents/page.tsx b/app/dashboard/adherents/page.tsx new file mode 100644 index 0000000..68c0de0 --- /dev/null +++ b/app/dashboard/adherents/page.tsx @@ -0,0 +1,27 @@ +import { redirect } from 'next/navigation'; +import { getCurrentUser } from '@/lib/auth'; +import DashboardLayout from '@/components/DashboardLayout'; +import AdherentsTable from '@/components/AdherentsTable'; + +export default async function AdherentsPage() { + const user = await getCurrentUser(); + + if (!user) { + redirect('/login'); + } + + return ( + +
+

+ Adhérents +

+

+ Base de données des adhérents +

+ + +
+
+ ); +} diff --git a/components/AdherentForm.tsx b/components/AdherentForm.tsx new file mode 100644 index 0000000..b364bba --- /dev/null +++ b/components/AdherentForm.tsx @@ -0,0 +1,427 @@ +'use client'; + +import { useState, useEffect } from 'react'; + +interface Adherent { + id: string; + nom: string; + prenom: string; + dateNaissance: string; + adresse: string; + email: string; + telephone: string; + situation?: string | null; + prescripteur?: string | null; + facturation?: string | null; + commentaire?: string | null; + telephoneSecondaire?: string | null; + instructions?: string | null; +} + +interface AdherentFormProps { + adherent: Adherent | null; + onClose: () => void; +} + +export default function AdherentForm({ adherent, onClose }: AdherentFormProps) { + const [loading, setLoading] = useState(false); + const [formData, setFormData] = useState({ + nom: '', + prenom: '', + dateNaissance: '', + adresse: '', + email: '', + telephone: '', + situation: '', + prescripteur: '', + facturation: '', + commentaire: '', + telephoneSecondaire: '', + instructions: '', + }); + + useEffect(() => { + if (adherent) { + const dateNaissance = new Date(adherent.dateNaissance); + setFormData({ + nom: adherent.nom, + prenom: adherent.prenom, + dateNaissance: dateNaissance.toISOString().split('T')[0], + adresse: adherent.adresse, + email: adherent.email, + telephone: adherent.telephone, + situation: adherent.situation || '', + prescripteur: adherent.prescripteur || '', + facturation: adherent.facturation || '', + commentaire: adherent.commentaire || '', + telephoneSecondaire: adherent.telephoneSecondaire || '', + instructions: adherent.instructions || '', + }); + } + }, [adherent]); + + const handleSubmit = async (e: React.FormEvent) => { + e.preventDefault(); + setLoading(true); + + try { + const url = adherent ? `/api/adherents/${adherent.id}` : '/api/adherents'; + const method = adherent ? 'PUT' : 'POST'; + + const payload = { + ...formData, + situation: formData.situation || null, + prescripteur: formData.prescripteur || null, + facturation: formData.facturation || null, + commentaire: formData.commentaire || null, + telephoneSecondaire: formData.telephoneSecondaire || null, + instructions: formData.instructions || null, + }; + + const response = await fetch(url, { + method, + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify(payload), + }); + + if (response.ok) { + onClose(); + } else { + const error = await response.json(); + alert(error.error || 'Une erreur est survenue'); + } + } catch (error) { + console.error('Erreur:', error); + alert('Une erreur est survenue'); + } finally { + setLoading(false); + } + }; + + return ( +
+
+
+
+

+ {adherent ? 'Modifier l\'adhérent' : 'Nouvel adhérent'} +

+

+ {adherent ? 'Modifiez les informations de l\'adhérent ci-dessous.' : 'Remplissez les informations pour créer un nouvel adhérent.'} +

+
+ +
+ +
+ {/* Informations principales */} +
+

+ Informations principales +

+
+
+ +
+
+ + + +
+ setFormData({ ...formData, nom: e.target.value })} + className="w-full pl-10 pr-3 py-2 border border-gray-300 rounded-lg text-gray-900 focus:outline-none focus:ring-2 focus:ring-lblue focus:border-transparent" + /> +
+
+ +
+ +
+
+ + + +
+ setFormData({ ...formData, prenom: e.target.value })} + className="w-full pl-10 pr-3 py-2 border border-gray-300 rounded-lg text-gray-900 focus:outline-none focus:ring-2 focus:ring-lblue focus:border-transparent" + /> +
+
+ +
+ +
+
+ + + +
+ setFormData({ ...formData, dateNaissance: e.target.value })} + className="w-full pl-10 pr-3 py-2 border border-gray-300 rounded-lg text-gray-900 focus:outline-none focus:ring-2 focus:ring-lblue focus:border-transparent" + /> +
+
+ +
+ +
+
+ + + +
+ setFormData({ ...formData, telephone: e.target.value })} + className="w-full pl-10 pr-3 py-2 border border-gray-300 rounded-lg text-gray-900 focus:outline-none focus:ring-2 focus:ring-lblue focus:border-transparent" + /> +
+
+ +
+ +
+
+ + + +
+ setFormData({ ...formData, email: e.target.value })} + className="w-full pl-10 pr-3 py-2 border border-gray-300 rounded-lg text-gray-900 focus:outline-none focus:ring-2 focus:ring-lblue focus:border-transparent" + /> +
+
+
+ +
+ +
+
+ + + + +
+ setFormData({ ...formData, adresse: e.target.value })} + className="w-full pl-10 pr-3 py-2 border border-gray-300 rounded-lg text-gray-900 focus:outline-none focus:ring-2 focus:ring-lblue focus:border-transparent" + /> +
+
+
+ + {/* Informations complémentaires */} +
+

+ Informations complémentaires +

+
+
+ +
+
+ + + +
+ +
+ + + +
+
+
+ +
+ +
+
+ + + +
+ +
+ + + +
+
+
+ +
+ +
+
+ + + +
+ +
+ + + +
+
+
+ +
+ +
+
+ + + +
+ setFormData({ ...formData, telephoneSecondaire: e.target.value })} + className="w-full pl-10 pr-3 py-2 border border-gray-300 rounded-lg text-gray-900 focus:outline-none focus:ring-2 focus:ring-lblue focus:border-transparent" + /> +
+
+
+ +
+ +