diff --git a/app/api/adherents/[id]/route.ts b/app/api/adherents/[id]/route.ts index 48de07e..187340d 100644 --- a/app/api/adherents/[id]/route.ts +++ b/app/api/adherents/[id]/route.ts @@ -46,7 +46,7 @@ export async function PUT( } const body = await request.json(); - const { nom, prenom, dateNaissance, adresse, email, telephone, situation, prescripteur, facturation, commentaire, telephoneSecondaire, instructions } = body; + const { nom, prenom, dateNaissance, adresse, email, telephone, situation, prescripteur, facturation, forfait, commentaire, telephoneSecondaire, instructions } = body; const updateData: any = {}; if (nom) updateData.nom = nom; @@ -58,6 +58,7 @@ export async function PUT( if (situation !== undefined) updateData.situation = situation || null; if (prescripteur !== undefined) updateData.prescripteur = prescripteur || null; if (facturation !== undefined) updateData.facturation = facturation || null; + if (forfait !== undefined) updateData.forfait = forfait || null; if (commentaire !== undefined) updateData.commentaire = commentaire || null; if (telephoneSecondaire !== undefined) updateData.telephoneSecondaire = telephoneSecondaire || null; if (instructions !== undefined) updateData.instructions = instructions || null; diff --git a/app/api/adherents/route.ts b/app/api/adherents/route.ts index f47aadb..e15c6eb 100644 --- a/app/api/adherents/route.ts +++ b/app/api/adherents/route.ts @@ -54,7 +54,7 @@ export async function POST(request: NextRequest) { } const body = await request.json(); - const { nom, prenom, dateNaissance, adresse, email, telephone, situation, prescripteur, facturation, commentaire, telephoneSecondaire, instructions } = body; + const { nom, prenom, dateNaissance, adresse, email, telephone, situation, prescripteur, facturation, forfait, commentaire, telephoneSecondaire, instructions } = body; if (!nom || !prenom || !dateNaissance || !adresse || !email || !telephone) { return NextResponse.json( @@ -74,6 +74,7 @@ export async function POST(request: NextRequest) { situation: situation || null, prescripteur: prescripteur || null, facturation: facturation || null, + forfait: forfait || null, commentaire: commentaire || null, telephoneSecondaire: telephoneSecondaire || null, instructions: instructions || null, diff --git a/app/api/settings/adherent-options/route.ts b/app/api/settings/adherent-options/route.ts index 8786bf5..5c9d7f2 100644 --- a/app/api/settings/adherent-options/route.ts +++ b/app/api/settings/adherent-options/route.ts @@ -11,7 +11,7 @@ export async function GET(request: NextRequest) { } const searchParams = request.nextUrl.searchParams; - const type = searchParams.get('type'); // "situation", "prescripteur", "facturation" + const type = searchParams.get('type'); // "situation", "prescripteur", "facturation", "forfait" const where: any = {}; if (type) { @@ -64,9 +64,9 @@ export async function POST(request: NextRequest) { ); } - if (!['situation', 'prescripteur', 'facturation'].includes(type)) { + if (!['situation', 'prescripteur', 'facturation', 'forfait'].includes(type)) { return NextResponse.json( - { error: 'Type invalide. Doit être: situation, prescripteur ou facturation' }, + { error: 'Type invalide. Doit être: situation, prescripteur, facturation ou forfait' }, { status: 400 } ); } diff --git a/app/api/trajets/[id]/route.ts b/app/api/trajets/[id]/route.ts index df8e403..3a0893e 100644 --- a/app/api/trajets/[id]/route.ts +++ b/app/api/trajets/[id]/route.ts @@ -22,8 +22,10 @@ export async function GET( nom: true, prenom: true, telephone: true, + telephoneSecondaire: true, email: true, adresse: true, + forfait: true, }, }, chauffeur: { @@ -64,7 +66,7 @@ export async function PUT( } const body = await request.json(); - const { date, adresseDepart, adresseArrivee, commentaire, statut, adherentId, chauffeurId } = body; + const { date, adresseDepart, adresseArrivee, commentaire, instructions, statut, adherentId, chauffeurId } = body; const trajet = await prisma.trajet.update({ where: { id: params.id }, @@ -73,6 +75,7 @@ export async function PUT( ...(adresseDepart && { adresseDepart }), ...(adresseArrivee && { adresseArrivee }), ...(commentaire !== undefined && { commentaire }), + ...(instructions !== undefined && { instructions }), ...(statut && { statut }), ...(adherentId && { adherentId }), ...(chauffeurId !== undefined && { chauffeurId }), @@ -84,7 +87,9 @@ export async function PUT( nom: true, prenom: true, telephone: true, + telephoneSecondaire: true, email: true, + forfait: true, }, }, chauffeur: { diff --git a/app/api/trajets/route.ts b/app/api/trajets/route.ts index 43b18b7..9d71873 100644 --- a/app/api/trajets/route.ts +++ b/app/api/trajets/route.ts @@ -45,7 +45,9 @@ export async function GET(request: NextRequest) { nom: true, prenom: true, telephone: true, + telephoneSecondaire: true, email: true, + forfait: true, }, }, chauffeur: { @@ -80,7 +82,7 @@ export async function POST(request: NextRequest) { } const body = await request.json(); - const { date, adresseDepart, adresseArrivee, commentaire, statut, adherentId, chauffeurId } = body; + const { date, adresseDepart, adresseArrivee, commentaire, instructions, statut, adherentId, chauffeurId } = body; if (!date || !adresseDepart || !adresseArrivee || !adherentId) { return NextResponse.json( @@ -95,6 +97,7 @@ export async function POST(request: NextRequest) { adresseDepart, adresseArrivee, commentaire: commentaire || null, + instructions: instructions || null, statut: statut || 'Planifié', adherentId, chauffeurId: chauffeurId || null, @@ -106,7 +109,9 @@ export async function POST(request: NextRequest) { nom: true, prenom: true, telephone: true, + telephoneSecondaire: true, email: true, + forfait: true, }, }, chauffeur: { diff --git a/components/AdherentForm.tsx b/components/AdherentForm.tsx index 5fda6ff..6e26cef 100644 --- a/components/AdherentForm.tsx +++ b/components/AdherentForm.tsx @@ -13,6 +13,7 @@ interface Adherent { situation?: string | null; prescripteur?: string | null; facturation?: string | null; + forfait?: string | null; commentaire?: string | null; telephoneSecondaire?: string | null; instructions?: string | null; @@ -29,10 +30,12 @@ export default function AdherentForm({ adherent, onClose }: AdherentFormProps) { situation: Array<{ id: string; value: string }>; prescripteur: Array<{ id: string; value: string }>; facturation: Array<{ id: string; value: string }>; + forfait: Array<{ id: string; value: string }>; }>({ situation: [], prescripteur: [], facturation: [], + forfait: [], }); const [formData, setFormData] = useState({ nom: '', @@ -44,6 +47,7 @@ export default function AdherentForm({ adherent, onClose }: AdherentFormProps) { situation: '', prescripteur: '', facturation: '', + forfait: '', commentaire: '', telephoneSecondaire: '', instructions: '', @@ -62,6 +66,7 @@ export default function AdherentForm({ adherent, onClose }: AdherentFormProps) { situation: data.situation || [], prescripteur: data.prescripteur || [], facturation: data.facturation || [], + forfait: data.forfait || [], }); } } catch (error) { @@ -82,6 +87,7 @@ export default function AdherentForm({ adherent, onClose }: AdherentFormProps) { situation: adherent.situation || '', prescripteur: adherent.prescripteur || '', facturation: adherent.facturation || '', + forfait: adherent.forfait || '', commentaire: adherent.commentaire || '', telephoneSecondaire: adherent.telephoneSecondaire || '', instructions: adherent.instructions || '', @@ -102,6 +108,7 @@ export default function AdherentForm({ adherent, onClose }: AdherentFormProps) { situation: formData.situation || null, prescripteur: formData.prescripteur || null, facturation: formData.facturation || null, + forfait: formData.forfait || null, commentaire: formData.commentaire || null, telephoneSecondaire: formData.telephoneSecondaire || null, instructions: formData.instructions || null, @@ -386,6 +393,37 @@ export default function AdherentForm({ adherent, onClose }: AdherentFormProps) { +
+ +
+
+ + + +
+ +
+ + + +
+
+
+
) : activeConfigSection === 'comptes' ? ( diff --git a/components/TrajetDetailModal.tsx b/components/TrajetDetailModal.tsx index 8dbeaae..415d5d1 100644 --- a/components/TrajetDetailModal.tsx +++ b/components/TrajetDetailModal.tsx @@ -1,24 +1,38 @@ 'use client'; import { useState } from 'react'; +import dynamic from 'next/dynamic'; import TrajetForm from './TrajetForm'; import ValidationModal from './ValidationModal'; import ConfirmModal from './ConfirmModal'; import { useNotification } from './NotificationProvider'; +// Import dynamique pour éviter les problèmes SSR avec Leaflet +const TrajetMap = dynamic(() => import('./TrajetMap'), { + ssr: false, + loading: () => ( +
+
Chargement de la carte...
+
+ ) +}); + interface Trajet { id: string; date: string; adresseDepart: string; adresseArrivee: string; commentaire?: string | null; + instructions?: string | null; statut: string; adherent: { id: string; nom: string; prenom: string; telephone: string; + telephoneSecondaire?: string | null; email: string; + forfait?: string | null; }; chauffeur?: { id: string; @@ -141,6 +155,33 @@ export default function TrajetDetailModal({ trajet, onClose, onUpdate }: TrajetD } }; + const handleCopyAddress = async (address: string) => { + try { + await navigator.clipboard.writeText(address); + showNotification('success', 'Adresse copiée dans le presse-papiers'); + } catch (error) { + console.error('Erreur lors de la copie:', error); + showNotification('error', 'Erreur lors de la copie de l\'adresse'); + } + }; + + const handleOpenRoute = () => { + const encodedDepart = encodeURIComponent(trajet.adresseDepart); + const encodedArrivee = encodeURIComponent(trajet.adresseArrivee); + + // Détecter le système d'exploitation pour ouvrir dans la bonne app + const userAgent = navigator.userAgent || navigator.vendor || (window as any).opera; + const isIOS = /iPad|iPhone|iPod/.test(userAgent) && !(window as any).MSStream; + + if (isIOS) { + // Apple Maps avec itinéraire + window.open(`https://maps.apple.com/?saddr=${encodedDepart}&daddr=${encodedArrivee}`, '_blank'); + } else { + // Google Maps avec itinéraire (par défaut sur Android et autres) + window.open(`https://www.google.com/maps/dir/${encodedDepart}/${encodedArrivee}`, '_blank'); + } + }; + if (showEditForm) { return ( -
+
+
{/* Header */} -
-
-
-

Détails du trajet

-

Informations complètes du trajet

+
+
+
+
+

Détails du trajet

+ + {trajet.statut} + +
+

Informations complètes du trajet

@@ -191,29 +237,53 @@ export default function TrajetDetailModal({ trajet, onClose, onUpdate }: TrajetD
{/* Content */} -
-
- {/* Statut */} -
- Statut - - {trajet.statut} - -
- +
+
+ {/* Colonne gauche - Informations */} +
{/* Adhérent */}
-
-
- {getInitials(trajet.adherent.nom, trajet.adherent.prenom)} -
-
-
- {trajet.adherent.prenom} {trajet.adherent.nom} +
+
+
+ {getInitials(trajet.adherent.nom, trajet.adherent.prenom)}
-
{trajet.adherent.email}
-
{trajet.adherent.telephone}
+
+
+ {trajet.adherent.prenom} {trajet.adherent.nom} +
+
+
+
+
+ + + + {trajet.adherent.telephone} +
+ {trajet.adherent.telephoneSecondaire && ( +
+ + + + Secondaire: {trajet.adherent.telephoneSecondaire} +
+ )} +
+ + + + {trajet.adherent.email} +
+ {trajet.adherent.forfait && ( +
+ + + + Forfait: {trajet.adherent.forfait} € +
+ )}
@@ -221,139 +291,198 @@ export default function TrajetDetailModal({ trajet, onClose, onUpdate }: TrajetD {/* Chauffeur */} {trajet.chauffeur ? (
- -
-
+ +
+
{getInitials(trajet.chauffeur.nom, trajet.chauffeur.prenom)}
-
-
+
+
{trajet.chauffeur.prenom} {trajet.chauffeur.nom}
-
{trajet.chauffeur.telephone}
+ + {trajet.chauffeur.telephone} +
) : (
- -
+ +
- + - Aucun chauffeur assigné + Aucun chauffeur assigné
)} {/* Date et heure */} -
+
- -
- + +
+ - {formatDate(trajet.date)} + {formatDate(trajet.date)}
- -
- + +
+ - {formatTime(trajet.date)} + {formatTime(trajet.date)}
{/* Adresses */}
- -
-
-
+ +
+
+
A
- {trajet.adresseDepart} + {trajet.adresseDepart} +
+
+
- -
-
-
+ +
+
+
B
- {trajet.adresseArrivee} + {trajet.adresseArrivee} +
+
+ +
- {/* Commentaire */} - {trajet.commentaire && ( + {/* Instructions */} + {trajet.instructions && (
- -
-

{trajet.commentaire}

+ +
+

{trajet.instructions}

)} + + {/* Commentaire */} + {trajet.commentaire && ( +
+ +
+

{trajet.commentaire}

+
+
+ )} +
+ + {/* Colonne droite - Carte */} +
+
+ +
+
{/* Footer */} -
-
+
+
-
+
{trajet.chauffeur && trajet.statut === 'Planifié' && ( )}
diff --git a/components/TrajetForm.tsx b/components/TrajetForm.tsx index 03ac123..aaa3b30 100644 --- a/components/TrajetForm.tsx +++ b/components/TrajetForm.tsx @@ -12,6 +12,13 @@ interface Adherent { adresse: string; telephone: string; email: string; + commentaire?: string | null; + telephoneSecondaire?: string | null; + instructions?: string | null; + situation?: string | null; + prescripteur?: string | null; + facturation?: string | null; + forfait?: string | null; } interface Chauffeur { @@ -31,6 +38,7 @@ interface TrajetFormProps { adresseDepart: string; adresseArrivee: string; commentaire?: string | null; + instructions?: string | null; statut: string; adherentId: string; chauffeurId?: string | null; @@ -55,6 +63,9 @@ export default function TrajetForm({ onClose, onSuccess, trajetToEdit }: TrajetF adherentPrenom: '', adherentAdresse: '', adherentTelephone: '', + adherentTelephoneSecondaire: '', + adherentEmail: '', + adherentForfait: '', chauffeurId: trajetToEdit?.chauffeurId || '', chauffeurNom: '', chauffeurPrenom: '', @@ -64,6 +75,7 @@ export default function TrajetForm({ onClose, onSuccess, trajetToEdit }: TrajetF adresseDepart: trajetToEdit?.adresseDepart || '', adresseArrivee: trajetToEdit?.adresseArrivee || '', commentaire: trajetToEdit?.commentaire || '', + instructions: trajetToEdit?.instructions || '', }); useEffect(() => { @@ -79,6 +91,33 @@ export default function TrajetForm({ onClose, onSuccess, trajetToEdit }: TrajetF .then(res => res.json()) .then(data => { if (data) { + // Construire le commentaire avec toutes les informations pertinentes + const commentaireParts: string[] = []; + + if (data.commentaire) { + commentaireParts.push(`Commentaire adhérent: ${data.commentaire}`); + } + + if (data.instructions) { + commentaireParts.push(`Instructions: ${data.instructions}`); + } + + if (data.telephoneSecondaire) { + commentaireParts.push(`Téléphone secondaire: ${data.telephoneSecondaire}`); + } + + if (data.situation) { + commentaireParts.push(`Situation: ${data.situation}`); + } + + if (data.prescripteur) { + commentaireParts.push(`Prescripteur: ${data.prescripteur}`); + } + + if (data.facturation) { + commentaireParts.push(`Facturation: ${data.facturation}`); + } + setFormData(prev => ({ ...prev, adherentId: data.id, @@ -86,7 +125,12 @@ export default function TrajetForm({ onClose, onSuccess, trajetToEdit }: TrajetF adherentPrenom: data.prenom, adherentAdresse: data.adresse, adherentTelephone: data.telephone, + adherentTelephoneSecondaire: data.telephoneSecondaire || '', + adherentEmail: data.email, + adherentForfait: data.forfait || '', adresseDepart: data.adresse, + commentaire: data.commentaire || prev.commentaire || trajetToEdit.commentaire || '', + instructions: data.instructions || prev.instructions || trajetToEdit.instructions || '', })); setSearchAdherent(`${data.prenom} ${data.nom}`); } @@ -161,16 +205,53 @@ export default function TrajetForm({ onClose, onSuccess, trajetToEdit }: TrajetF } }; - const handleSelectAdherent = (adherent: Adherent) => { - setFormData({ - ...formData, - adherentId: adherent.id, - adherentNom: adherent.nom, - adherentPrenom: adherent.prenom, - adherentAdresse: adherent.adresse, - adherentTelephone: adherent.telephone, - adresseDepart: adherent.adresse, // Remplir automatiquement l'adresse de départ - }); + const handleSelectAdherent = async (adherent: Adherent) => { + // Charger toutes les informations complètes de l'adhérent depuis l'API + try { + const response = await fetch(`/api/adherents/${adherent.id}`); + if (response.ok) { + const fullAdherent = await response.json(); + + setFormData({ + ...formData, + adherentId: fullAdherent.id, + adherentNom: fullAdherent.nom, + adherentPrenom: fullAdherent.prenom, + adherentAdresse: fullAdherent.adresse, + adherentTelephone: fullAdherent.telephone, + adherentTelephoneSecondaire: fullAdherent.telephoneSecondaire || '', + adherentEmail: fullAdherent.email, + adherentForfait: fullAdherent.forfait || '', + adresseDepart: fullAdherent.adresse, // Remplir automatiquement l'adresse de départ + commentaire: fullAdherent.commentaire || formData.commentaire || '', // Prendre uniquement le commentaire de l'adhérent + instructions: fullAdherent.instructions || formData.instructions || '', // Pré-remplir les instructions + }); + } else { + // Si l'API échoue, utiliser les données de base + setFormData({ + ...formData, + adherentId: adherent.id, + adherentNom: adherent.nom, + adherentPrenom: adherent.prenom, + adherentAdresse: adherent.adresse, + adherentTelephone: adherent.telephone, + adresseDepart: adherent.adresse, + }); + } + } catch (error) { + console.error('Erreur lors du chargement des détails de l\'adhérent:', error); + // En cas d'erreur, utiliser les données de base + setFormData({ + ...formData, + adherentId: adherent.id, + adherentNom: adherent.nom, + adherentPrenom: adherent.prenom, + adherentAdresse: adherent.adresse, + adherentTelephone: adherent.telephone, + adresseDepart: adherent.adresse, + }); + } + setSearchAdherent(`${adherent.prenom} ${adherent.nom}`); setShowAdherentDropdown(false); }; @@ -228,6 +309,7 @@ export default function TrajetForm({ onClose, onSuccess, trajetToEdit }: TrajetF adresseDepart: formData.adresseDepart, adresseArrivee: formData.adresseArrivee, commentaire: formData.commentaire || null, + instructions: formData.instructions || null, statut: trajetToEdit?.statut || 'Planifié', adherentId: formData.adherentId, chauffeurId: formData.chauffeurId || null, @@ -328,7 +410,7 @@ export default function TrajetForm({ onClose, onSuccess, trajetToEdit }: TrajetF )}
{formData.adherentId && ( -
+
{getInitials(formData.adherentNom, formData.adherentPrenom)} @@ -337,10 +419,39 @@ export default function TrajetForm({ onClose, onSuccess, trajetToEdit }: TrajetF
{formData.adherentPrenom} {formData.adherentNom}
-
{formData.adherentTelephone}
-
{formData.adherentAdresse}
+
{formData.adherentAdresse}
+
+
+ + + + {formData.adherentTelephone} +
+ {formData.adherentTelephoneSecondaire && ( +
+ + + + Secondaire: {formData.adherentTelephoneSecondaire} +
+ )} +
+ + + + {formData.adherentEmail} +
+ {formData.adherentForfait && ( +
+ + + + Forfait: {formData.adherentForfait} € +
+ )} +
)}
@@ -456,15 +567,39 @@ export default function TrajetForm({ onClose, onSuccess, trajetToEdit }: TrajetF />
+ {/* Instructions */} +
+ +