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)}
-
) : (
-
-
+
+
-
+
- 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}
+
+
)}
+
+ {/* 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 */}
+
+
+
+
{/* Commentaire */}
{/* Informations supplémentaires */}
- {(formData.date || formData.heure || formData.chauffeurId || formData.commentaire) && (
+ {(formData.date || formData.heure || formData.chauffeurId || formData.instructions || formData.commentaire) && (
{formData.date && (
@@ -520,10 +655,16 @@ export default function TrajetForm({ onClose, onSuccess, trajetToEdit }: TrajetF
)}
+ {formData.instructions && (
+
+
Instructions
+
{formData.instructions}
+
+ )}
{formData.commentaire && (
Commentaire
-
{formData.commentaire}
+
{formData.commentaire}
)}
diff --git a/components/TrajetMap.tsx b/components/TrajetMap.tsx
index c74b306..34c4fe2 100644
--- a/components/TrajetMap.tsx
+++ b/components/TrajetMap.tsx
@@ -2,10 +2,15 @@
import { useEffect, useState, useRef } from 'react';
import dynamic from 'next/dynamic';
-import L from 'leaflet';
+
+// Import conditionnel de Leaflet uniquement côté client
+let L: any;
+if (typeof window !== 'undefined') {
+ L = require('leaflet');
+}
// Fix pour les icônes Leaflet avec Next.js
-if (typeof window !== 'undefined') {
+if (typeof window !== 'undefined' && L) {
delete (L.Icon.Default.prototype as any)._getIconUrl;
L.Icon.Default.mergeOptions({
iconRetinaUrl: 'https://cdnjs.cloudflare.com/ajax/libs/leaflet/1.9.4/images/marker-icon-2x.png',
@@ -43,9 +48,14 @@ export default function TrajetMap({ adresseDepart, adresseArrivee, adherentNom }
const [routeInfo, setRouteInfo] = useState
(null);
const [loading, setLoading] = useState(false);
const [error, setError] = useState(null);
+ const [mounted, setMounted] = useState(false);
// Cache simple pour éviter de regéocoder les mêmes adresses
const geocodeCacheRef = useRef