From 1ec4c935c97a6421459fad398b75115e29648af3 Mon Sep 17 00:00:00 2001 From: Pierre Date: Mon, 16 Feb 2026 14:43:02 +0100 Subject: [PATCH] Added few functions --- app/api/trajets/[id]/route.ts | 3 + app/api/trajets/archives/route.ts | 3 + app/api/trajets/route.ts | 3 + app/dashboard/calendrier/page.tsx | 7 -- components/AdherentForm.tsx | 2 + components/AdherentsTable.tsx | 2 + components/AlertModal.tsx | 3 + components/ArchivesTrajets.tsx | 18 ++++- components/BudgetContent.tsx | 10 +++ components/CalendrierPageContent.tsx | 36 +++++++++- components/CalendrierTrajets.tsx | 7 ++ components/ChauffeursTable.tsx | 2 + components/ConfigurationContent.tsx | 5 ++ components/ConfirmModal.tsx | 3 + components/DashboardContent.tsx | 7 ++ components/DashboardLayout.tsx | 2 + components/GroupSettingsModal.tsx | 2 + components/ListeTrajets.tsx | 41 +++++++---- components/Messagerie.tsx | 2 + components/NewConversationModal.tsx | 2 + components/ParticipationEditModal.tsx | 2 + components/ParticipationFinanciereList.tsx | 10 ++- components/TrajetDetailModal.tsx | 9 +++ components/TrajetForm.tsx | 76 +++++++++++++++++++-- components/UniversProForm.tsx | 2 + components/UniversProTable.tsx | 2 + components/ValidationModal.tsx | 2 + lib/body-scroll-lock.ts | 45 ++++++++++++ lib/participation-ref.ts | 7 ++ lib/trajet-duree.ts | 72 +++++++++++++++++++ prisma/dev.db | Bin 270336 -> 270336 bytes 31 files changed, 352 insertions(+), 35 deletions(-) create mode 100644 lib/body-scroll-lock.ts create mode 100644 lib/participation-ref.ts create mode 100644 lib/trajet-duree.ts diff --git a/app/api/trajets/[id]/route.ts b/app/api/trajets/[id]/route.ts index a20b655..a2d48b3 100644 --- a/app/api/trajets/[id]/route.ts +++ b/app/api/trajets/[id]/route.ts @@ -39,6 +39,9 @@ export async function GET( email: true, }, }, + participations: { + select: { id: true }, + }, }, }); diff --git a/app/api/trajets/archives/route.ts b/app/api/trajets/archives/route.ts index 905a2b6..3c55524 100644 --- a/app/api/trajets/archives/route.ts +++ b/app/api/trajets/archives/route.ts @@ -35,6 +35,9 @@ export async function GET(request: NextRequest) { telephone: true, }, }, + participations: { + select: { id: true }, + }, }, orderBy: { updatedAt: 'desc' as const, diff --git a/app/api/trajets/route.ts b/app/api/trajets/route.ts index aa5b4d3..7f35522 100644 --- a/app/api/trajets/route.ts +++ b/app/api/trajets/route.ts @@ -59,6 +59,9 @@ export async function GET(request: NextRequest) { telephone: true, }, }, + participations: { + select: { id: true }, + }, }, orderBy, take: limit ? parseInt(limit) : undefined, diff --git a/app/dashboard/calendrier/page.tsx b/app/dashboard/calendrier/page.tsx index 46f8bda..2c571d3 100644 --- a/app/dashboard/calendrier/page.tsx +++ b/app/dashboard/calendrier/page.tsx @@ -19,13 +19,6 @@ export default async function CalendrierPage() { return (
-

- Calendrier -

-

- Gestion des trajets et planning des chauffeurs -

-
diff --git a/components/AdherentForm.tsx b/components/AdherentForm.tsx index 5c3171d..da21f45 100644 --- a/components/AdherentForm.tsx +++ b/components/AdherentForm.tsx @@ -2,6 +2,7 @@ import { useState, useEffect } from 'react'; import AlertModal from './AlertModal'; +import { useBodyScrollLock } from '@/lib/body-scroll-lock'; interface Adherent { id: string; @@ -26,6 +27,7 @@ interface AdherentFormProps { } export default function AdherentForm({ adherent, onClose }: AdherentFormProps) { + useBodyScrollLock(true); const [loading, setLoading] = useState(false); const [isMobile, setIsMobile] = useState(false); const [alertModal, setAlertModal] = useState<{ diff --git a/components/AdherentsTable.tsx b/components/AdherentsTable.tsx index 954bd08..07c4a0a 100644 --- a/components/AdherentsTable.tsx +++ b/components/AdherentsTable.tsx @@ -1,6 +1,7 @@ 'use client'; import { useState, useEffect } from 'react'; +import { useBodyScrollLock } from '@/lib/body-scroll-lock'; import AdherentForm from './AdherentForm'; import ConfirmModal from './ConfirmModal'; @@ -41,6 +42,7 @@ export default function AdherentsTable() { show: boolean; id: string | null; } | null>(null); + useBodyScrollLock(showImportModal || !!(resultModal?.show) || !!viewingAdherent); const fetchAdherents = async (searchTerm: string = '') => { setLoading(true); diff --git a/components/AlertModal.tsx b/components/AlertModal.tsx index 322f505..3a1a7d9 100644 --- a/components/AlertModal.tsx +++ b/components/AlertModal.tsx @@ -1,5 +1,7 @@ 'use client'; +import { useBodyScrollLock } from '@/lib/body-scroll-lock'; + interface AlertModalProps { isOpen: boolean; type: 'success' | 'error' | 'info' | 'warning'; @@ -15,6 +17,7 @@ export default function AlertModal({ message, onClose, }: AlertModalProps) { + useBodyScrollLock(isOpen); if (!isOpen) return null; const getStyles = () => { diff --git a/components/ArchivesTrajets.tsx b/components/ArchivesTrajets.tsx index 85b592a..dd9ee04 100644 --- a/components/ArchivesTrajets.tsx +++ b/components/ArchivesTrajets.tsx @@ -3,6 +3,7 @@ import { useState, useEffect } from 'react'; import { useNotification } from './NotificationProvider'; import ConfirmModal from './ConfirmModal'; +import { getParticipationRef } from '@/lib/participation-ref'; interface Trajet { id: string; @@ -12,6 +13,7 @@ interface Trajet { commentaire?: string | null; statut: string; archived: boolean; + participations?: { id: string }[]; adherent: { id: string; nom: string; @@ -117,14 +119,21 @@ export default function ArchivesTrajets() { return `${prenom.charAt(0)}${nom.charAt(0)}`.toUpperCase(); }; + const participationRef = (t: Trajet) => { + const p = t.participations?.[0]; + return p ? getParticipationRef(p.id) : null; + }; + const filteredTrajets = trajets.filter((trajet) => { + const ref = participationRef(trajet); const searchLower = searchTerm.toLowerCase(); return ( trajet.adherent.nom.toLowerCase().includes(searchLower) || trajet.adherent.prenom.toLowerCase().includes(searchLower) || trajet.adresseDepart.toLowerCase().includes(searchLower) || trajet.adresseArrivee.toLowerCase().includes(searchLower) || - (trajet.chauffeur && `${trajet.chauffeur.prenom} ${trajet.chauffeur.nom}`.toLowerCase().includes(searchLower)) + (trajet.chauffeur && `${trajet.chauffeur.prenom} ${trajet.chauffeur.nom}`.toLowerCase().includes(searchLower)) || + (ref && ref.toLowerCase().includes(searchLower)) ); }); @@ -144,7 +153,7 @@ export default function ArchivesTrajets() {
setSearchTerm(e.target.value)} className="w-full px-3 md:px-4 py-2 md:py-2.5 pl-9 md:pl-10 text-sm md:text-base border border-gray-300 rounded-lg focus:ring-2 focus:ring-lblue focus:border-transparent" @@ -188,6 +197,11 @@ export default function ArchivesTrajets() { {formatDate(trajet.date)} à {formatTime(trajet.date)}
+ {participationRef(trajet) && ( + + {participationRef(trajet)} + + )} {trajet.statut} diff --git a/components/BudgetContent.tsx b/components/BudgetContent.tsx index e7f8598..f2baee4 100644 --- a/components/BudgetContent.tsx +++ b/components/BudgetContent.tsx @@ -3,6 +3,8 @@ import React, { useState, useEffect } from 'react'; import { useNotification } from './NotificationProvider'; import Link from 'next/link'; +import { getParticipationRef } from '@/lib/participation-ref'; +import { useBodyScrollLock } from '@/lib/body-scroll-lock'; interface HistoriqueItem { id: string; @@ -41,6 +43,7 @@ export default function BudgetContent() { const [rectifierBudget, setRectifierBudget] = useState(''); const [rectifierAjustement, setRectifierAjustement] = useState(''); const [expandedPrescripteur, setExpandedPrescripteur] = useState(null); + useBodyScrollLock(!!addModalPrescripteur || !!rectifierModalPrescripteur); useEffect(() => { fetchBudgets(); @@ -332,6 +335,7 @@ export default function BudgetContent() { Date + Référence Adhérent Montant Lien @@ -341,6 +345,11 @@ export default function BudgetContent() { {item.historique.map((h) => ( {formatDate(h.date)} + + + {getParticipationRef(h.id)} + + {h.adherentNom} {formatEuro(h.montant)} @@ -455,6 +464,7 @@ export default function BudgetContent() {

{h.adherentNom}

{formatDate(h.date)}

+

{getParticipationRef(h.id)}

{formatEuro(h.montant)} diff --git a/components/CalendrierPageContent.tsx b/components/CalendrierPageContent.tsx index 4a01dac..89fc5ca 100644 --- a/components/CalendrierPageContent.tsx +++ b/components/CalendrierPageContent.tsx @@ -3,9 +3,11 @@ import { useState } from 'react'; import CalendrierTrajets from './CalendrierTrajets'; import ListeTrajets from './ListeTrajets'; +import TrajetForm from './TrajetForm'; export default function CalendrierPageContent() { const [refreshTrigger, setRefreshTrigger] = useState(0); + const [showTrajetForm, setShowTrajetForm] = useState(false); const handleTrajetCreated = () => { setRefreshTrigger((prev) => prev + 1); @@ -13,6 +15,27 @@ export default function CalendrierPageContent() { return (
+ {/* En-tête avec titre et bouton */} +
+
+

+ Calendrier +

+

+ Gestion des trajets et planning des chauffeurs +

+
+ +
+ {/* Calendrier en haut */}
@@ -20,8 +43,19 @@ export default function CalendrierPageContent() { {/* Liste des trajets en bas, triable par période */}
- +
+ + {/* Modal formulaire trajet */} + {showTrajetForm && ( + setShowTrajetForm(false)} + onSuccess={() => { + handleTrajetCreated(); + setShowTrajetForm(false); + }} + /> + )}
); } diff --git a/components/CalendrierTrajets.tsx b/components/CalendrierTrajets.tsx index f3c210b..7f00972 100644 --- a/components/CalendrierTrajets.tsx +++ b/components/CalendrierTrajets.tsx @@ -5,6 +5,7 @@ import { DndContext, DragEndEvent, DragOverlay, DragStartEvent, useDraggable, us import { CSS } from '@dnd-kit/utilities'; import TrajetDetailModal from './TrajetDetailModal'; import { useNotification } from './NotificationProvider'; +import { getParticipationRef } from '@/lib/participation-ref'; interface Trajet { id: string; @@ -13,6 +14,7 @@ interface Trajet { adresseArrivee: string; commentaire?: string | null; statut: string; + participations?: { id: string }[]; adherent: { id: string; nom: string; @@ -518,6 +520,11 @@ export default function CalendrierTrajets({ refreshTrigger }: CalendrierTrajetsP {trajet.adherent.prenom} {trajet.adherent.nom} + {trajet.participations?.[0] && ( + + {getParticipationRef(trajet.participations[0].id)} + + )} {formatTime(trajet.date)} diff --git a/components/ChauffeursTable.tsx b/components/ChauffeursTable.tsx index 596cc96..bcb9fbc 100644 --- a/components/ChauffeursTable.tsx +++ b/components/ChauffeursTable.tsx @@ -3,6 +3,7 @@ import { useState, useEffect } from 'react'; import ChauffeurForm from './ChauffeurForm'; import ConfirmModal from './ConfirmModal'; +import { useBodyScrollLock } from '@/lib/body-scroll-lock'; interface Chauffeur { id: string; @@ -39,6 +40,7 @@ export default function ChauffeursTable() { show: boolean; id: string | null; } | null>(null); + useBodyScrollLock(showImportModal || !!(resultModal?.show) || !!viewingChauffeur); const fetchChauffeurs = async (searchTerm: string = '') => { setLoading(true); diff --git a/components/ConfigurationContent.tsx b/components/ConfigurationContent.tsx index 1321f39..1fcc68c 100644 --- a/components/ConfigurationContent.tsx +++ b/components/ConfigurationContent.tsx @@ -5,6 +5,7 @@ import { useRouter } from 'next/navigation'; import { useNotification } from './NotificationProvider'; import { AVAILABLE_PAGES } from '@/lib/pages'; import ConfirmModal from './ConfirmModal'; +import { useBodyScrollLock } from '@/lib/body-scroll-lock'; interface AdherentOption { id: string; @@ -200,6 +201,8 @@ export default function ConfigurationContent() { roleId: string | null; } | null>(null); + useBodyScrollLock(isMobile); + const fetchOptions = useCallback(async () => { setLoading(true); try { @@ -373,6 +376,7 @@ export default function ConfigurationContent() { const [selectedUser, setSelectedUser] = useState(null); const [showPasswordModal, setShowPasswordModal] = useState(false); const [newPassword, setNewPassword] = useState(null); + useBodyScrollLock(showPasswordModal && !!newPassword); useEffect(() => { fetchUsers(); @@ -688,6 +692,7 @@ export default function ConfigurationContent() { description: '', pageRoutes: [] as string[], }); + useBodyScrollLock(showRoleForm); useEffect(() => { fetchRoles(); diff --git a/components/ConfirmModal.tsx b/components/ConfirmModal.tsx index d2307ae..076aef1 100644 --- a/components/ConfirmModal.tsx +++ b/components/ConfirmModal.tsx @@ -1,5 +1,7 @@ 'use client'; +import { useBodyScrollLock } from '@/lib/body-scroll-lock'; + interface ConfirmModalProps { isOpen: boolean; title: string; @@ -21,6 +23,7 @@ export default function ConfirmModal({ onConfirm, onCancel, }: ConfirmModalProps) { + useBodyScrollLock(isOpen); if (!isOpen) return null; const getConfirmButtonStyle = () => { diff --git a/components/DashboardContent.tsx b/components/DashboardContent.tsx index 6aa8369..de45dde 100644 --- a/components/DashboardContent.tsx +++ b/components/DashboardContent.tsx @@ -5,6 +5,7 @@ import { useRouter } from 'next/navigation'; import TrajetForm from './TrajetForm'; import AdherentForm from './AdherentForm'; import TrajetDetailModal from './TrajetDetailModal'; +import { getParticipationRef } from '@/lib/participation-ref'; interface Stats { participationsMois: { @@ -32,6 +33,7 @@ interface Trajet { adresseArrivee: string; commentaire?: string | null; statut: string; + participations?: { id: string }[]; adherent: { id: string; nom: string; @@ -361,6 +363,11 @@ export default function DashboardContent({ userName }: DashboardContentProps) { {trajet.adherent.prenom} {trajet.adherent.nom}
+ {trajet.participations?.[0] && ( + + {getParticipationRef(trajet.participations[0].id)} + + )} diff --git a/components/DashboardLayout.tsx b/components/DashboardLayout.tsx index bc44626..e1dc390 100644 --- a/components/DashboardLayout.tsx +++ b/components/DashboardLayout.tsx @@ -3,6 +3,7 @@ import { useRouter, usePathname } from 'next/navigation'; import { useState } from 'react'; import Image from 'next/image'; +import { useBodyScrollLock } from '@/lib/body-scroll-lock'; import Link from 'next/link'; import useSWR from 'swr'; @@ -34,6 +35,7 @@ export default function DashboardLayout({ user, children }: DashboardLayoutProps const [showNotifications, setShowNotifications] = useState(false); const [showProfileMenu, setShowProfileMenu] = useState(false); const [sidebarOpen, setSidebarOpen] = useState(false); + useBodyScrollLock(sidebarOpen); // Récupérer les conversations pour compter les messages non lus const { data: conversations } = useSWR>( diff --git a/components/GroupSettingsModal.tsx b/components/GroupSettingsModal.tsx index af12b68..152e26a 100644 --- a/components/GroupSettingsModal.tsx +++ b/components/GroupSettingsModal.tsx @@ -4,6 +4,7 @@ import { useState, useEffect } from 'react'; import useSWR from 'swr'; import AlertModal from './AlertModal'; import ConfirmModal from './ConfirmModal'; +import { useBodyScrollLock } from '@/lib/body-scroll-lock'; interface User { id: string; @@ -38,6 +39,7 @@ export default function GroupSettingsModal({ onClose, onUpdate, }: GroupSettingsModalProps) { + useBodyScrollLock(true); const [groupName, setGroupName] = useState(conversation.name || ''); const [search, setSearch] = useState(''); const [selectedUsers, setSelectedUsers] = useState([]); diff --git a/components/ListeTrajets.tsx b/components/ListeTrajets.tsx index 0350b82..70a05a1 100644 --- a/components/ListeTrajets.tsx +++ b/components/ListeTrajets.tsx @@ -2,6 +2,7 @@ import { useState, useEffect } from 'react'; import TrajetForm from './TrajetForm'; +import { getParticipationRef } from '@/lib/participation-ref'; interface Trajet { id: string; @@ -10,6 +11,7 @@ interface Trajet { adresseArrivee: string; commentaire?: string | null; statut: string; + participations?: { id: string }[]; adherent: { id: string; nom: string; @@ -30,9 +32,10 @@ type FilterPeriod = 'derniers' | 'jour' | 'mois' | 'an' | 'personnalise'; interface ListeTrajetsProps { onTrajetCreated?: () => void; compact?: boolean; + hideNewTrajetButton?: boolean; } -export default function ListeTrajets({ onTrajetCreated, compact }: ListeTrajetsProps) { +export default function ListeTrajets({ onTrajetCreated, compact, hideNewTrajetButton }: ListeTrajetsProps) { const [trajets, setTrajets] = useState([]); const [loading, setLoading] = useState(true); const [search, setSearch] = useState(''); @@ -119,7 +122,13 @@ export default function ListeTrajets({ onTrajetCreated, compact }: ListeTrajetsP } }; + const participationRef = (t: Trajet) => { + const p = t.participations?.[0]; + return p ? getParticipationRef(p.id) : null; + }; + const filteredTrajets = trajets.filter((trajet) => { + const ref = participationRef(trajet); const matchesSearch = !search || trajet.adherent.nom.toLowerCase().includes(search.toLowerCase()) || @@ -129,7 +138,8 @@ export default function ListeTrajets({ onTrajetCreated, compact }: ListeTrajetsP (trajet.chauffeur && `${trajet.chauffeur.prenom} ${trajet.chauffeur.nom}` .toLowerCase() - .includes(search.toLowerCase())); + .includes(search.toLowerCase())) || + (ref && ref.toLowerCase().includes(search.toLowerCase())); const matchesStatut = !filterStatut || trajet.statut === filterStatut; @@ -180,7 +190,7 @@ export default function ListeTrajets({ onTrajetCreated, compact }: ListeTrajetsP
setSearch(e.target.value)} className="block w-full pl-9 pr-3 py-2 text-sm border border-gray-300 rounded-lg bg-white text-gray-900 placeholder-gray-400 focus:outline-none focus:ring-2 focus:ring-lblue focus:border-lblue transition-all" @@ -272,15 +282,17 @@ export default function ListeTrajets({ onTrajetCreated, compact }: ListeTrajetsP {/* Actions */}
- + {!hideNewTrajetButton && ( + + )}