Added optimizations for mobile
This commit is contained in:
@@ -18,11 +18,11 @@ export default async function AdherentsPage() {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<DashboardLayout user={user}>
|
<DashboardLayout user={user}>
|
||||||
<div className="p-6">
|
<div className="p-4 md:p-6">
|
||||||
<h1 className="text-3xl font-semibold text-cblack mb-1">
|
<h1 className="text-2xl md:text-3xl font-semibold text-cblack mb-1">
|
||||||
Adhérents
|
Adhérents
|
||||||
</h1>
|
</h1>
|
||||||
<p className="text-sm text-cgray mb-6">
|
<p className="text-xs md:text-sm text-cgray mb-4 md:mb-6">
|
||||||
Base de données des adhérents
|
Base de données des adhérents
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
|
|||||||
@@ -17,9 +17,9 @@ export default async function ArchivesPage() {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<DashboardLayout user={user}>
|
<DashboardLayout user={user}>
|
||||||
<div className="p-8">
|
<div className="p-4 md:p-6 lg:p-8">
|
||||||
<h1 className="text-3xl font-semibold text-cblack mb-2">Archives</h1>
|
<h1 className="text-2xl md:text-3xl font-semibold text-cblack mb-2">Archives</h1>
|
||||||
<p className="text-sm text-cgray mb-8">Trajets archivés</p>
|
<p className="text-xs md:text-sm text-cgray mb-6 md:mb-8">Trajets archivés</p>
|
||||||
<ArchivesTrajets />
|
<ArchivesTrajets />
|
||||||
</div>
|
</div>
|
||||||
</DashboardLayout>
|
</DashboardLayout>
|
||||||
|
|||||||
@@ -18,11 +18,11 @@ export default async function CalendrierPage() {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<DashboardLayout user={user}>
|
<DashboardLayout user={user}>
|
||||||
<div className="p-8">
|
<div className="p-4 sm:p-6 lg:p-8">
|
||||||
<h1 className="text-3xl font-semibold text-cblack mb-2">
|
<h1 className="text-2xl sm:text-3xl font-semibold text-cblack mb-2">
|
||||||
Calendrier
|
Calendrier
|
||||||
</h1>
|
</h1>
|
||||||
<p className="text-sm text-cgray mb-8">
|
<p className="text-xs sm:text-sm text-cgray mb-6 sm:mb-8">
|
||||||
Gestion des trajets et planning des chauffeurs
|
Gestion des trajets et planning des chauffeurs
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
|
|||||||
@@ -18,11 +18,11 @@ export default async function ChauffeursPage() {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<DashboardLayout user={user}>
|
<DashboardLayout user={user}>
|
||||||
<div className="p-6">
|
<div className="p-4 md:p-6">
|
||||||
<h1 className="text-3xl font-semibold text-cblack mb-1">
|
<h1 className="text-2xl md:text-3xl font-semibold text-cblack mb-1">
|
||||||
Chauffeurs
|
Chauffeurs
|
||||||
</h1>
|
</h1>
|
||||||
<p className="text-sm text-cgray mb-6">
|
<p className="text-xs md:text-sm text-cgray mb-4 md:mb-6">
|
||||||
Base de données des chauffeurs
|
Base de données des chauffeurs
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
|
|||||||
@@ -18,11 +18,11 @@ export default async function UniversProPage() {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<DashboardLayout user={user}>
|
<DashboardLayout user={user}>
|
||||||
<div className="p-6">
|
<div className="p-4 md:p-6">
|
||||||
<h1 className="text-3xl font-semibold text-cblack mb-1">
|
<h1 className="text-2xl md:text-3xl font-semibold text-cblack mb-1">
|
||||||
Univers Pro
|
Univers Pro
|
||||||
</h1>
|
</h1>
|
||||||
<p className="text-sm text-cgray mb-6">
|
<p className="text-xs md:text-sm text-cgray mb-4 md:mb-6">
|
||||||
Base de données des contacts professionnels
|
Base de données des contacts professionnels
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
|
|||||||
@@ -16,49 +16,50 @@ export default function Error({ error, reset }: ErrorProps) {
|
|||||||
}, [error]);
|
}, [error]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="min-h-screen flex items-center justify-center bg-cwhite">
|
<div className="min-h-screen flex items-center justify-center bg-cwhite px-4 py-8">
|
||||||
<div className="text-center px-4">
|
<div className="text-center w-full max-w-md mx-auto">
|
||||||
{/* Logo */}
|
{/* Logo */}
|
||||||
<div className="flex justify-center mb-8">
|
<div className="flex justify-center mb-6 md:mb-8">
|
||||||
<Image
|
<Image
|
||||||
src="/logo.svg"
|
src="/logo.svg"
|
||||||
alt="MAD Logo"
|
alt="MAD Logo"
|
||||||
width={120}
|
width={100}
|
||||||
height={120}
|
height={100}
|
||||||
|
className="w-20 h-20 md:w-[120px] md:h-[120px]"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* 500 Content */}
|
{/* 500 Content */}
|
||||||
<div className="max-w-md mx-auto">
|
<div className="max-w-md mx-auto">
|
||||||
<h1 className="text-9xl font-bold text-lorange mb-4">500</h1>
|
<h1 className="text-7xl md:text-9xl font-bold text-lorange mb-3 md:mb-4">500</h1>
|
||||||
<h2 className="text-3xl font-semibold text-gray-900 mb-4">
|
<h2 className="text-2xl md:text-3xl font-semibold text-gray-900 mb-3 md:mb-4">
|
||||||
Erreur serveur
|
Erreur serveur
|
||||||
</h2>
|
</h2>
|
||||||
<p className="text-gray-600 mb-2">
|
<p className="text-sm md:text-base text-gray-600 mb-2 px-2">
|
||||||
Une erreur inattendue s'est produite sur le serveur.
|
Une erreur inattendue s'est produite sur le serveur.
|
||||||
</p>
|
</p>
|
||||||
{error.message && (
|
{error.message && (
|
||||||
<p className="text-sm text-gray-500 mb-8 font-mono bg-gray-100 p-3 rounded-lg">
|
<p className="text-xs md:text-sm text-gray-500 mb-6 md:mb-8 font-mono bg-gray-100 p-2 md:p-3 rounded-lg break-all text-left mt-3">
|
||||||
{error.message}
|
{error.message}
|
||||||
</p>
|
</p>
|
||||||
)}
|
)}
|
||||||
{!error.message && (
|
{!error.message && (
|
||||||
<p className="text-gray-600 mb-8">
|
<p className="text-sm md:text-base text-gray-600 mb-6 md:mb-8 px-2">
|
||||||
Veuillez réessayer dans quelques instants.
|
Veuillez réessayer dans quelques instants.
|
||||||
</p>
|
</p>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{/* Action Buttons */}
|
{/* Action Buttons */}
|
||||||
<div className="flex flex-col sm:flex-row gap-4 justify-center">
|
<div className="flex flex-col gap-3 md:gap-4 justify-center">
|
||||||
<button
|
<button
|
||||||
onClick={reset}
|
onClick={reset}
|
||||||
className="inline-flex items-center justify-center px-6 py-3 border border-transparent rounded-lg text-sm font-medium text-white bg-lorange hover:bg-dorange focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-lorange transition-colors"
|
className="inline-flex items-center justify-center px-6 py-3 border border-transparent rounded-lg text-sm font-medium text-white bg-lorange hover:bg-dorange focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-lorange transition-colors w-full md:w-auto"
|
||||||
>
|
>
|
||||||
Réessayer
|
Réessayer
|
||||||
</button>
|
</button>
|
||||||
<Link
|
<Link
|
||||||
href="/dashboard"
|
href="/dashboard"
|
||||||
className="inline-flex items-center justify-center px-6 py-3 border border-gray-300 rounded-lg text-sm font-medium text-gray-700 bg-white hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-lorange transition-colors"
|
className="inline-flex items-center justify-center px-6 py-3 border border-gray-300 rounded-lg text-sm font-medium text-gray-700 bg-white hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-lorange transition-colors w-full md:w-auto"
|
||||||
>
|
>
|
||||||
Retour au tableau de bord
|
Retour au tableau de bord
|
||||||
</Link>
|
</Link>
|
||||||
@@ -66,8 +67,8 @@ export default function Error({ error, reset }: ErrorProps) {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Footer */}
|
{/* Footer */}
|
||||||
<div className="mt-12 text-center text-xs text-gray-500">
|
<div className="mt-8 md:mt-12 text-center text-xs text-gray-500 px-4">
|
||||||
© {new Date().getFullYear()} MAD - <a href="https://legouix.dev" target="_blank" className="text-lblue hover:text-dblue">Propulsé par LGX</a>
|
© {new Date().getFullYear()} MAD - <a href="https://legouix.dev" target="_blank" rel="noopener noreferrer" className="text-lblue hover:text-dblue">Propulsé par LGX</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -11,25 +11,26 @@ export default async function LoginPage() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="min-h-screen flex items-center justify-center bg-cwhite">
|
<div className="min-h-screen flex items-center justify-center bg-cwhite px-4 sm:px-6 lg:px-8 py-8 sm:py-12">
|
||||||
<div className="w-full max-w-lg bg-white rounded-3xl shadow-lg py-8 px-12">
|
<div className="w-full max-w-lg bg-white rounded-2xl sm:rounded-3xl shadow-lg py-6 sm:py-8 px-6 sm:px-8 md:px-12">
|
||||||
{/* Logo */}
|
{/* Logo */}
|
||||||
<div className="flex justify-center mb-6">
|
<div className="flex justify-center mb-4 sm:mb-6">
|
||||||
<Image
|
<Image
|
||||||
src="/logo.svg"
|
src="/logo.svg"
|
||||||
alt="MAD Logo"
|
alt="MAD Logo"
|
||||||
width={120}
|
width={100}
|
||||||
height={120}
|
height={100}
|
||||||
|
className="w-20 h-20 sm:w-24 sm:h-24 md:w-[120px] md:h-[120px]"
|
||||||
priority
|
priority
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Heading */}
|
{/* Heading */}
|
||||||
<div className="text-center mb-6">
|
<div className="text-center mb-5 sm:mb-6">
|
||||||
<h2 className="text-2xl font-bold text-gray-900 mb-2">
|
<h2 className="text-xl sm:text-2xl font-bold text-gray-900 mb-2">
|
||||||
Content de vous revoir
|
Content de vous revoir
|
||||||
</h2>
|
</h2>
|
||||||
<p className="text-sm text-gray-600">
|
<p className="text-xs sm:text-sm text-gray-600 px-2">
|
||||||
Connectez-vous pour accéder à la plateforme.
|
Connectez-vous pour accéder à la plateforme.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
@@ -38,7 +39,7 @@ export default async function LoginPage() {
|
|||||||
<LoginForm />
|
<LoginForm />
|
||||||
|
|
||||||
{/* Footer */}
|
{/* Footer */}
|
||||||
<div className="mt-8 text-center text-xs text-gray-500">
|
<div className="mt-6 sm:mt-8 text-center text-[10px] sm:text-xs text-gray-500 px-2">
|
||||||
© {new Date().getFullYear()} MAD - <a href="https://legouix.dev" target="_blank" className="text-lblue hover:text-dblue">Propulsé par LGX</a>
|
© {new Date().getFullYear()} MAD - <a href="https://legouix.dev" target="_blank" className="text-lblue hover:text-dblue">Propulsé par LGX</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -3,39 +3,40 @@ import Image from 'next/image';
|
|||||||
|
|
||||||
export default function NotFound() {
|
export default function NotFound() {
|
||||||
return (
|
return (
|
||||||
<div className="min-h-screen flex items-center justify-center bg-cwhite">
|
<div className="min-h-screen flex items-center justify-center bg-cwhite px-4 py-8">
|
||||||
<div className="text-center px-4">
|
<div className="text-center w-full max-w-md mx-auto">
|
||||||
{/* Logo */}
|
{/* Logo */}
|
||||||
<div className="flex justify-center mb-8">
|
<div className="flex justify-center mb-6 md:mb-8">
|
||||||
<Image
|
<Image
|
||||||
src="/logo.svg"
|
src="/logo.svg"
|
||||||
alt="MAD Logo"
|
alt="MAD Logo"
|
||||||
width={120}
|
width={100}
|
||||||
height={120}
|
height={100}
|
||||||
|
className="w-20 h-20 md:w-[120px] md:h-[120px]"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* 404 Content */}
|
{/* 404 Content */}
|
||||||
<div className="max-w-md mx-auto">
|
<div className="max-w-md mx-auto">
|
||||||
<h1 className="text-9xl font-bold text-lblue mb-4">404</h1>
|
<h1 className="text-7xl md:text-9xl font-bold text-lblue mb-3 md:mb-4">404</h1>
|
||||||
<h2 className="text-3xl font-semibold text-gray-900 mb-4">
|
<h2 className="text-2xl md:text-3xl font-semibold text-gray-900 mb-3 md:mb-4">
|
||||||
Page non trouvée
|
Page non trouvée
|
||||||
</h2>
|
</h2>
|
||||||
<p className="text-gray-600 mb-8">
|
<p className="text-sm md:text-base text-gray-600 mb-6 md:mb-8 px-2">
|
||||||
Désolé, la page que vous recherchez n'existe pas ou a été déplacée.
|
Désolé, la page que vous recherchez n'existe pas ou a été déplacée.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
{/* Action Buttons */}
|
{/* Action Buttons */}
|
||||||
<div className="flex flex-col sm:flex-row gap-4 justify-center">
|
<div className="flex flex-col gap-3 md:gap-4 justify-center">
|
||||||
<Link
|
<Link
|
||||||
href="/dashboard"
|
href="/dashboard"
|
||||||
className="inline-flex items-center justify-center px-6 py-3 border border-transparent rounded-lg text-sm font-medium text-white bg-lblue hover:bg-dblue focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-lblue transition-colors"
|
className="inline-flex items-center justify-center px-6 py-3 border border-transparent rounded-lg text-sm font-medium text-white bg-lblue hover:bg-dblue focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-lblue transition-colors w-full md:w-auto"
|
||||||
>
|
>
|
||||||
Retour au tableau de bord
|
Retour au tableau de bord
|
||||||
</Link>
|
</Link>
|
||||||
<Link
|
<Link
|
||||||
href="/login"
|
href="/login"
|
||||||
className="inline-flex items-center justify-center px-6 py-3 border border-gray-300 rounded-lg text-sm font-medium text-gray-700 bg-white hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-lblue transition-colors"
|
className="inline-flex items-center justify-center px-6 py-3 border border-gray-300 rounded-lg text-sm font-medium text-gray-700 bg-white hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-lblue transition-colors w-full md:w-auto"
|
||||||
>
|
>
|
||||||
Page de connexion
|
Page de connexion
|
||||||
</Link>
|
</Link>
|
||||||
@@ -43,8 +44,8 @@ export default function NotFound() {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Footer */}
|
{/* Footer */}
|
||||||
<div className="mt-12 text-center text-xs text-gray-500">
|
<div className="mt-8 md:mt-12 text-center text-xs text-gray-500 px-4">
|
||||||
© {new Date().getFullYear()} MAD - <a href="https://legouix.dev" target="_blank" className="text-lblue hover:text-dblue">Propulsé par LGX</a>
|
© {new Date().getFullYear()} MAD - <a href="https://legouix.dev" target="_blank" rel="noopener noreferrer" className="text-lblue hover:text-dblue">Propulsé par LGX</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -27,6 +27,7 @@ interface AdherentFormProps {
|
|||||||
|
|
||||||
export default function AdherentForm({ adherent, onClose }: AdherentFormProps) {
|
export default function AdherentForm({ adherent, onClose }: AdherentFormProps) {
|
||||||
const [loading, setLoading] = useState(false);
|
const [loading, setLoading] = useState(false);
|
||||||
|
const [isMobile, setIsMobile] = useState(false);
|
||||||
const [alertModal, setAlertModal] = useState<{
|
const [alertModal, setAlertModal] = useState<{
|
||||||
show: boolean;
|
show: boolean;
|
||||||
type: 'success' | 'error' | 'info' | 'warning';
|
type: 'success' | 'error' | 'info' | 'warning';
|
||||||
@@ -64,6 +65,17 @@ export default function AdherentForm({ adherent, onClose }: AdherentFormProps) {
|
|||||||
fetchOptions();
|
fetchOptions();
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const checkMobile = () => {
|
||||||
|
setIsMobile(window.innerWidth < 768); // md breakpoint
|
||||||
|
};
|
||||||
|
|
||||||
|
checkMobile();
|
||||||
|
window.addEventListener('resize', checkMobile);
|
||||||
|
|
||||||
|
return () => window.removeEventListener('resize', checkMobile);
|
||||||
|
}, []);
|
||||||
|
|
||||||
const fetchOptions = async () => {
|
const fetchOptions = async () => {
|
||||||
try {
|
try {
|
||||||
const response = await fetch('/api/settings/adherent-options');
|
const response = await fetch('/api/settings/adherent-options');
|
||||||
@@ -153,6 +165,51 @@ export default function AdherentForm({ adherent, onClose }: AdherentFormProps) {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Si on est sur mobile, afficher un message au lieu du formulaire
|
||||||
|
if (isMobile) {
|
||||||
|
return (
|
||||||
|
<div className="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50 p-4">
|
||||||
|
<div className="bg-white rounded-lg p-6 max-w-md w-full mx-4">
|
||||||
|
<div className="flex justify-between items-start mb-6">
|
||||||
|
<div className="flex-1">
|
||||||
|
<h2 className="text-xl font-semibold text-gray-900 mb-2">
|
||||||
|
{adherent ? 'Modifier l\'adhérent' : 'Nouvel adhérent'}
|
||||||
|
</h2>
|
||||||
|
</div>
|
||||||
|
<button
|
||||||
|
onClick={onClose}
|
||||||
|
className="text-gray-400 hover:text-gray-600 ml-4"
|
||||||
|
>
|
||||||
|
<svg className="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M6 18L18 6M6 6l12 12" />
|
||||||
|
</svg>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="text-center py-8">
|
||||||
|
<div className="mb-6">
|
||||||
|
<svg className="w-16 h-16 mx-auto text-gray-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9.75 17L9 20l-1 1h8l-1-1-.75-3M3 13h18M5 17h14a2 2 0 002-2V5a2 2 0 00-2-2H5a2 2 0 00-2 2v10a2 2 0 002 2z" />
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
<h3 className="text-lg font-semibold text-gray-900 mb-3">
|
||||||
|
Utilisez un ordinateur
|
||||||
|
</h3>
|
||||||
|
<p className="text-sm text-gray-600 mb-6">
|
||||||
|
Pour {adherent ? 'modifier' : 'créer'} un adhérent, veuillez utiliser un ordinateur. Cette fonctionnalité n'est pas optimisée pour les appareils mobiles.
|
||||||
|
</p>
|
||||||
|
<button
|
||||||
|
onClick={onClose}
|
||||||
|
className="px-6 py-2 bg-lblue text-white rounded-lg hover:bg-dblue transition-colors"
|
||||||
|
>
|
||||||
|
Fermer
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50">
|
<div className="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50">
|
||||||
<div className="bg-white rounded-lg p-6 max-w-4xl w-full mx-4 max-h-[90vh] overflow-y-auto">
|
<div className="bg-white rounded-lg p-6 max-w-4xl w-full mx-4 max-h-[90vh] overflow-y-auto">
|
||||||
|
|||||||
@@ -722,50 +722,54 @@ export default function AdherentsTable() {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Boutons d'action */}
|
{/* Boutons d'action */}
|
||||||
<div className="flex gap-3">
|
<div className="flex gap-2 md:gap-3 w-full md:w-auto">
|
||||||
<button
|
<button
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setEditingAdherent(null);
|
setEditingAdherent(null);
|
||||||
setShowForm(true);
|
setShowForm(true);
|
||||||
}}
|
}}
|
||||||
className="flex items-center gap-2 px-4 py-2 bg-lgreen text-white rounded-lg hover:bg-dgreen transition-colors"
|
className="flex items-center gap-1 md:gap-2 px-3 md:px-4 py-2 bg-lgreen text-white rounded-lg hover:bg-dgreen transition-colors text-sm md:text-base flex-1 md:flex-none justify-center"
|
||||||
>
|
>
|
||||||
<svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
<svg className="w-4 h-4 md:w-5 md:h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M12 4v16m8-8H4" />
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M12 4v16m8-8H4" />
|
||||||
</svg>
|
</svg>
|
||||||
Nouvel adhérent
|
<span className="hidden sm:inline">Nouvel adhérent</span>
|
||||||
|
<span className="sm:hidden">Nouveau</span>
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
onClick={() => setShowImportModal(true)}
|
onClick={() => setShowImportModal(true)}
|
||||||
className="flex items-center gap-2 px-4 py-2 bg-lblue text-white rounded-lg hover:bg-dblue transition-colors"
|
className="flex items-center gap-1 md:gap-2 px-3 md:px-4 py-2 bg-lblue text-white rounded-lg hover:bg-dblue transition-colors text-sm md:text-base"
|
||||||
>
|
>
|
||||||
<svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
<svg className="w-4 h-4 md:w-5 md:h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M4 16v1a3 3 0 003 3h10a3 3 0 003-3v-1m-4-4l-4 4m0 0l-4-4m4 4V4" />
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M4 16v1a3 3 0 003 3h10a3 3 0 003-3v-1m-4-4l-4 4m0 0l-4-4m4 4V4" />
|
||||||
</svg>
|
</svg>
|
||||||
Importer
|
<span className="hidden sm:inline">Importer</span>
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
onClick={handleExport}
|
onClick={handleExport}
|
||||||
disabled={selectedIds.size === 0}
|
disabled={selectedIds.size === 0}
|
||||||
className="flex items-center gap-2 px-4 py-2 bg-lorange text-white rounded-lg hover:bg-dorange transition-colors disabled:opacity-50 disabled:cursor-not-allowed"
|
className="flex items-center gap-1 md:gap-2 px-3 md:px-4 py-2 bg-lorange text-white rounded-lg hover:bg-dorange transition-colors disabled:opacity-50 disabled:cursor-not-allowed text-sm md:text-base"
|
||||||
>
|
>
|
||||||
<svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
<svg className="w-4 h-4 md:w-5 md:h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M4 16v1a3 3 0 003 3h10a3 3 0 003-3v-1m-4-4l-4-4m0 0l-4-4m4 4V4" />
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M4 16v1a3 3 0 003 3h10a3 3 0 003-3v-1m-4-4l-4-4m0 0l-4-4m4 4V4" />
|
||||||
</svg>
|
</svg>
|
||||||
Exporter {selectedIds.size > 0 && `(${selectedIds.size})`}
|
<span className="hidden sm:inline">Exporter</span>
|
||||||
|
{selectedIds.size > 0 && <span className="ml-1">({selectedIds.size})</span>}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Tableau */}
|
{/* Tableau - Desktop */}
|
||||||
<div className="bg-white rounded-lg shadow-sm overflow-hidden">
|
<div className="bg-white rounded-lg shadow-sm overflow-hidden">
|
||||||
{loading ? (
|
{loading ? (
|
||||||
<div className="p-8 text-center text-gray-500">Chargement...</div>
|
<div className="p-8 text-center text-gray-500">Chargement...</div>
|
||||||
) : adherents.length === 0 ? (
|
) : adherents.length === 0 ? (
|
||||||
<div className="p-8 text-center text-gray-500">Aucun adhérent trouvé</div>
|
<div className="p-8 text-center text-gray-500">Aucun adhérent trouvé</div>
|
||||||
) : (
|
) : (
|
||||||
<div className="overflow-x-auto">
|
<>
|
||||||
|
{/* Vue desktop - Tableau */}
|
||||||
|
<div className="hidden md:block overflow-x-auto">
|
||||||
<table className="min-w-full divide-y divide-gray-200">
|
<table className="min-w-full divide-y divide-gray-200">
|
||||||
<thead className="bg-gray-50">
|
<thead className="bg-gray-50">
|
||||||
<tr>
|
<tr>
|
||||||
@@ -885,6 +889,114 @@ export default function AdherentsTable() {
|
|||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{/* Vue mobile - Cartes */}
|
||||||
|
<div className="md:hidden divide-y divide-gray-200">
|
||||||
|
{adherents.map((adherent) => (
|
||||||
|
<div key={adherent.id} className="p-4 hover:bg-gray-50">
|
||||||
|
<div className="flex items-start gap-3">
|
||||||
|
{/* Checkbox */}
|
||||||
|
<input
|
||||||
|
type="checkbox"
|
||||||
|
checked={selectedIds.has(adherent.id)}
|
||||||
|
onChange={(e) => handleSelectOne(adherent.id, e.target.checked)}
|
||||||
|
className="w-4 h-4 text-lblue border-gray-300 rounded focus:ring-lblue mt-1"
|
||||||
|
/>
|
||||||
|
|
||||||
|
{/* Avatar */}
|
||||||
|
<div className="w-12 h-12 rounded-full bg-lgreen flex items-center justify-center text-white font-semibold flex-shrink-0">
|
||||||
|
{getInitials(adherent.nom, adherent.prenom)}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Contenu principal */}
|
||||||
|
<div className="flex-1 min-w-0">
|
||||||
|
{/* Nom et date de naissance */}
|
||||||
|
<div className="mb-2">
|
||||||
|
<div className="text-base font-semibold text-gray-900">
|
||||||
|
{adherent.prenom} {adherent.nom}
|
||||||
|
</div>
|
||||||
|
<div className="text-xs text-gray-500">
|
||||||
|
Né le {formatDate(adherent.dateNaissance)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Contact */}
|
||||||
|
<div className="mb-2">
|
||||||
|
<div className="text-xs font-medium text-gray-500 uppercase tracking-wide mb-1">Contact</div>
|
||||||
|
<a href={`tel:${adherent.telephone}`} className="text-sm text-gray-900 block truncate">
|
||||||
|
{adherent.telephone} <span className="text-xs text-gray-500">(Principal)</span>
|
||||||
|
</a>
|
||||||
|
{adherent.telephoneSecondaire && (
|
||||||
|
<a href={`tel:${adherent.telephoneSecondaire}`} className="text-sm text-gray-600 block truncate">
|
||||||
|
{adherent.telephoneSecondaire} <span className="text-xs text-gray-500">(Secondaire)</span>
|
||||||
|
</a>
|
||||||
|
)}
|
||||||
|
<a href={`mailto:${adherent.email}`} className="text-sm text-gray-500 block truncate">
|
||||||
|
{adherent.email}
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Adresse */}
|
||||||
|
<div className="mb-2">
|
||||||
|
<div className="text-xs font-medium text-gray-500 uppercase tracking-wide mb-1">Adresse</div>
|
||||||
|
<div className="text-sm text-gray-900 line-clamp-2">
|
||||||
|
{adherent.adresse}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Prescripteur et Situation */}
|
||||||
|
<div className="mb-3 flex flex-wrap gap-3">
|
||||||
|
{adherent.prescripteur && (
|
||||||
|
<div>
|
||||||
|
<div className="text-xs font-medium text-gray-500 uppercase tracking-wide mb-1">Prescripteur</div>
|
||||||
|
<div className="text-sm text-gray-900">{adherent.prescripteur}</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
{adherent.situation && (
|
||||||
|
<div>
|
||||||
|
<div className="text-xs font-medium text-gray-500 uppercase tracking-wide mb-1">Situation</div>
|
||||||
|
<div className="text-sm text-gray-900">{adherent.situation}</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Actions */}
|
||||||
|
<div className="flex items-center gap-4 pt-2 border-t border-gray-200">
|
||||||
|
<button
|
||||||
|
onClick={() => handleView(adherent.id)}
|
||||||
|
className="flex items-center gap-1 text-lblue hover:text-dblue text-sm"
|
||||||
|
>
|
||||||
|
<svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M15 12a3 3 0 11-6 0 3 3 0 016 0z" />
|
||||||
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M2.458 12C3.732 7.943 7.523 5 12 5c4.478 0 8.268 2.943 9.542 7-1.274 4.057-5.064 7-9.542 7-4.477 0-8.268-2.943-9.542-7z" />
|
||||||
|
</svg>
|
||||||
|
Voir
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
onClick={() => handleEdit(adherent)}
|
||||||
|
className="flex items-center gap-1 text-lblue hover:text-dblue text-sm"
|
||||||
|
>
|
||||||
|
<svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M11 5H6a2 2 0 00-2 2v11a2 2 0 002 2h11a2 2 0 002-2v-5m-1.414-9.414a2 2 0 112.828 2.828L11.828 15H9v-2.828l8.586-8.586z" />
|
||||||
|
</svg>
|
||||||
|
Modifier
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
onClick={() => handleDelete(adherent.id)}
|
||||||
|
className="flex items-center gap-1 text-red-500 hover:text-red-700 text-sm ml-auto"
|
||||||
|
>
|
||||||
|
<svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16" />
|
||||||
|
</svg>
|
||||||
|
Supprimer
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -1134,29 +1246,29 @@ export default function AdherentsTable() {
|
|||||||
|
|
||||||
{/* Modal vue détaillée - Design épuré */}
|
{/* Modal vue détaillée - Design épuré */}
|
||||||
{viewingAdherent && (
|
{viewingAdherent && (
|
||||||
<div className="fixed inset-0 bg-black/60 backdrop-blur-sm flex items-center justify-center z-50 p-4 animate-fadeIn">
|
<div className="fixed inset-0 bg-black/60 backdrop-blur-sm flex items-center justify-center z-50 p-0 md:p-4 animate-fadeIn">
|
||||||
<div className="bg-white rounded-xl shadow-2xl max-w-5xl w-full max-h-[95vh] overflow-hidden flex flex-col animate-slideUp border border-gray-200">
|
<div className="bg-white rounded-none md:rounded-xl shadow-2xl max-w-5xl w-full h-full md:h-auto md:max-h-[95vh] overflow-hidden flex flex-col animate-slideUp border-0 md:border border-gray-200">
|
||||||
{/* Header épuré */}
|
{/* Header épuré */}
|
||||||
<div className="border-b border-gray-200 px-8 py-6 bg-white">
|
<div className="border-b border-gray-200 px-4 md:px-8 py-4 md:py-6 bg-white">
|
||||||
<div className="flex items-start justify-between">
|
<div className="flex items-start justify-between">
|
||||||
<div className="flex items-center gap-5">
|
<div className="flex items-center gap-3 md:gap-5 flex-1 min-w-0">
|
||||||
<div className="w-16 h-16 rounded-full bg-lblue flex items-center justify-center text-white font-bold text-xl border-4 border-lblue/10 shadow-sm">
|
<div className="w-12 h-12 md:w-16 md:h-16 rounded-full bg-lblue flex items-center justify-center text-white font-bold text-lg md:text-xl border-2 md:border-4 border-lblue/10 shadow-sm flex-shrink-0">
|
||||||
{getInitials(viewingAdherent.nom, viewingAdherent.prenom)}
|
{getInitials(viewingAdherent.nom, viewingAdherent.prenom)}
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div className="flex-1 min-w-0">
|
||||||
<h2 className="text-2xl font-bold text-gray-900 mb-1">
|
<h2 className="text-xl md:text-2xl font-bold text-gray-900 mb-1 truncate">
|
||||||
{viewingAdherent.prenom} {viewingAdherent.nom}
|
{viewingAdherent.prenom} {viewingAdherent.nom}
|
||||||
</h2>
|
</h2>
|
||||||
<p className="text-gray-500 text-sm">
|
<p className="text-gray-500 text-xs md:text-sm">
|
||||||
Informations détaillées de l'adhérent
|
Informations détaillées de l'adhérent
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<button
|
<button
|
||||||
onClick={() => setViewingAdherent(null)}
|
onClick={() => setViewingAdherent(null)}
|
||||||
className="text-gray-400 hover:text-gray-600 transition-colors p-2 hover:bg-gray-100 rounded-lg"
|
className="text-gray-400 hover:text-gray-600 transition-colors p-2 hover:bg-gray-100 rounded-lg flex-shrink-0 ml-2"
|
||||||
>
|
>
|
||||||
<svg className="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
<svg className="w-5 h-5 md:w-6 md:h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M6 18L18 6M6 6l12 12" />
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M6 18L18 6M6 6l12 12" />
|
||||||
</svg>
|
</svg>
|
||||||
</button>
|
</button>
|
||||||
@@ -1165,57 +1277,57 @@ export default function AdherentsTable() {
|
|||||||
|
|
||||||
{/* Contenu scrollable */}
|
{/* Contenu scrollable */}
|
||||||
<div className="flex-1 overflow-y-auto">
|
<div className="flex-1 overflow-y-auto">
|
||||||
<div className="p-8">
|
<div className="p-4 md:p-8">
|
||||||
{/* Actions rapides */}
|
{/* Actions rapides */}
|
||||||
<div className="mb-8 flex flex-wrap gap-3">
|
<div className="mb-6 md:mb-8 flex flex-wrap gap-2 md:gap-3">
|
||||||
<a
|
<a
|
||||||
href={`tel:${viewingAdherent.telephone}`}
|
href={`tel:${viewingAdherent.telephone}`}
|
||||||
className="flex items-center gap-2 px-4 py-2 bg-gray-50 text-gray-700 border border-gray-200 rounded-lg hover:bg-gray-100 hover:border-lblue transition-colors"
|
className="flex items-center gap-2 px-3 md:px-4 py-2 bg-gray-50 text-gray-700 border border-gray-200 rounded-lg hover:bg-gray-100 hover:border-lblue transition-colors flex-1 md:flex-none justify-center"
|
||||||
>
|
>
|
||||||
<svg className="w-5 h-5 text-lblue" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
<svg className="w-4 h-4 md:w-5 md:h-5 text-lblue" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M3 5a2 2 0 012-2h3.28a1 1 0 01.948.684l1.498 4.493a1 1 0 01-.502 1.21l-2.257 1.13a11.042 11.042 0 005.516 5.516l1.13-2.257a1 1 0 011.21-.502l4.493 1.498a1 1 0 01.684.949V19a2 2 0 01-2 2h-1C9.716 21 3 14.284 3 6V5z" />
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M3 5a2 2 0 012-2h3.28a1 1 0 01.948.684l1.498 4.493a1 1 0 01-.502 1.21l-2.257 1.13a11.042 11.042 0 005.516 5.516l1.13-2.257a1 1 0 011.21-.502l4.493 1.498a1 1 0 01.684.949V19a2 2 0 01-2 2h-1C9.716 21 3 14.284 3 6V5z" />
|
||||||
</svg>
|
</svg>
|
||||||
<span className="text-sm font-medium">Appeler</span>
|
<span className="text-xs md:text-sm font-medium">Appeler</span>
|
||||||
</a>
|
</a>
|
||||||
{viewingAdherent.telephoneSecondaire && (
|
{viewingAdherent.telephoneSecondaire && (
|
||||||
<a
|
<a
|
||||||
href={`tel:${viewingAdherent.telephoneSecondaire}`}
|
href={`tel:${viewingAdherent.telephoneSecondaire}`}
|
||||||
className="flex items-center gap-2 px-4 py-2 bg-gray-50 text-gray-700 border border-gray-200 rounded-lg hover:bg-gray-100 hover:border-lblue transition-colors"
|
className="flex items-center gap-2 px-3 md:px-4 py-2 bg-gray-50 text-gray-700 border border-gray-200 rounded-lg hover:bg-gray-100 hover:border-lblue transition-colors flex-1 md:flex-none justify-center"
|
||||||
>
|
>
|
||||||
<svg className="w-5 h-5 text-lblue" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
<svg className="w-4 h-4 md:w-5 md:h-5 text-lblue" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M3 5a2 2 0 012-2h3.28a1 1 0 01.948.684l1.498 4.493a1 1 0 01-.502 1.21l-2.257 1.13a11.042 11.042 0 005.516 5.516l1.13-2.257a1 1 0 011.21-.502l4.493 1.498a1 1 0 01.684.949V19a2 2 0 01-2 2h-1C9.716 21 3 14.284 3 6V5z" />
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M3 5a2 2 0 012-2h3.28a1 1 0 01.948.684l1.498 4.493a1 1 0 01-.502 1.21l-2.257 1.13a11.042 11.042 0 005.516 5.516l1.13-2.257a1 1 0 011.21-.502l4.493 1.498a1 1 0 01.684.949V19a2 2 0 01-2 2h-1C9.716 21 3 14.284 3 6V5z" />
|
||||||
</svg>
|
</svg>
|
||||||
<span className="text-sm font-medium">Téléphone secondaire</span>
|
<span className="text-xs md:text-sm font-medium">Téléphone secondaire</span>
|
||||||
</a>
|
</a>
|
||||||
)}
|
)}
|
||||||
<a
|
<a
|
||||||
href={`mailto:${viewingAdherent.email}`}
|
href={`mailto:${viewingAdherent.email}`}
|
||||||
className="flex items-center gap-2 px-4 py-2 bg-gray-50 text-gray-700 border border-gray-200 rounded-lg hover:bg-gray-100 hover:border-lblue transition-colors"
|
className="flex items-center gap-2 px-3 md:px-4 py-2 bg-gray-50 text-gray-700 border border-gray-200 rounded-lg hover:bg-gray-100 hover:border-lblue transition-colors flex-1 md:flex-none justify-center"
|
||||||
>
|
>
|
||||||
<svg className="w-5 h-5 text-lblue" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
<svg className="w-4 h-4 md:w-5 md:h-5 text-lblue" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M3 8l7.89 5.26a2 2 0 002.22 0L21 8M5 19h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v10a2 2 0 002 2z" />
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M3 8l7.89 5.26a2 2 0 002.22 0L21 8M5 19h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v10a2 2 0 002 2z" />
|
||||||
</svg>
|
</svg>
|
||||||
<span className="text-sm font-medium">Envoyer un email</span>
|
<span className="text-xs md:text-sm font-medium">Envoyer un email</span>
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="grid grid-cols-1 lg:grid-cols-2 gap-6">
|
<div className="grid grid-cols-1 lg:grid-cols-2 gap-4 md:gap-6">
|
||||||
{/* Carte Informations principales */}
|
{/* Carte Informations principales */}
|
||||||
<div className="bg-white rounded-xl border border-gray-200 p-6 shadow-sm">
|
<div className="bg-white rounded-lg md:rounded-xl border border-gray-200 p-4 md:p-6 shadow-sm">
|
||||||
<div className="flex items-center gap-3 mb-6 pb-4 border-b border-gray-200">
|
<div className="flex items-center gap-2 md:gap-3 mb-4 md:mb-6 pb-3 md:pb-4 border-b border-gray-200">
|
||||||
<div className="w-10 h-10 rounded-lg bg-lblue/10 flex items-center justify-center">
|
<div className="w-8 h-8 md:w-10 md:h-10 rounded-lg bg-lblue/10 flex items-center justify-center flex-shrink-0">
|
||||||
<svg className="w-5 h-5 text-lblue" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
<svg className="w-4 h-4 md:w-5 md:h-5 text-lblue" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M16 7a4 4 0 11-8 0 4 4 0 018 0zM12 14a7 7 0 00-7 7h14a7 7 0 00-7-7z" />
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M16 7a4 4 0 11-8 0 4 4 0 018 0zM12 14a7 7 0 00-7 7h14a7 7 0 00-7-7z" />
|
||||||
</svg>
|
</svg>
|
||||||
</div>
|
</div>
|
||||||
<h3 className="text-lg font-semibold text-gray-900">
|
<h3 className="text-base md:text-lg font-semibold text-gray-900">
|
||||||
Informations principales
|
Informations principales
|
||||||
</h3>
|
</h3>
|
||||||
</div>
|
</div>
|
||||||
<div className="space-y-4">
|
<div className="space-y-3 md:space-y-4">
|
||||||
<div className="flex items-start gap-4">
|
<div className="flex items-start gap-3 md:gap-4">
|
||||||
<div className="w-10 h-10 rounded-lg bg-gray-100 flex items-center justify-center flex-shrink-0">
|
<div className="w-8 h-8 md:w-10 md:h-10 rounded-lg bg-gray-100 flex items-center justify-center flex-shrink-0">
|
||||||
<svg className="w-5 h-5 text-gray-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
<svg className="w-4 h-4 md:w-5 md:h-5 text-gray-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M8 7V3m8 4V3m-9 8h10M5 21h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v12a2 2 0 002 2z" />
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M8 7V3m8 4V3m-9 8h10M5 21h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v12a2 2 0 002 2z" />
|
||||||
</svg>
|
</svg>
|
||||||
</div>
|
</div>
|
||||||
@@ -1225,39 +1337,39 @@ export default function AdherentsTable() {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="flex items-start gap-4">
|
<div className="flex items-start gap-3 md:gap-4">
|
||||||
<div className="w-10 h-10 rounded-lg bg-gray-100 flex items-center justify-center flex-shrink-0">
|
<div className="w-8 h-8 md:w-10 md:h-10 rounded-lg bg-gray-100 flex items-center justify-center flex-shrink-0">
|
||||||
<svg className="w-5 h-5 text-gray-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
<svg className="w-4 h-4 md:w-5 md:h-5 text-gray-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M3 5a2 2 0 012-2h3.28a1 1 0 01.948.684l1.498 4.493a1 1 0 01-.502 1.21l-2.257 1.13a11.042 11.042 0 005.516 5.516l1.13-2.257a1 1 0 011.21-.502l4.493 1.498a1 1 0 01.684.949V19a2 2 0 01-2 2h-1C9.716 21 3 14.284 3 6V5z" />
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M3 5a2 2 0 012-2h3.28a1 1 0 01.948.684l1.498 4.493a1 1 0 01-.502 1.21l-2.257 1.13a11.042 11.042 0 005.516 5.516l1.13-2.257a1 1 0 011.21-.502l4.493 1.498a1 1 0 01.684.949V19a2 2 0 01-2 2h-1C9.716 21 3 14.284 3 6V5z" />
|
||||||
</svg>
|
</svg>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex-1 min-w-0">
|
<div className="flex-1 min-w-0">
|
||||||
<p className="text-xs font-medium text-gray-500 uppercase tracking-wide mb-1">Téléphone</p>
|
<p className="text-xs font-medium text-gray-500 uppercase tracking-wide mb-1">Téléphone</p>
|
||||||
<a href={`tel:${viewingAdherent.telephone}`} className="text-sm font-semibold text-lblue hover:text-dblue transition-colors">
|
<a href={`tel:${viewingAdherent.telephone}`} className="text-sm font-semibold text-lblue hover:text-dblue transition-colors break-all">
|
||||||
{viewingAdherent.telephone}
|
{viewingAdherent.telephone}
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{viewingAdherent.telephoneSecondaire && (
|
{viewingAdherent.telephoneSecondaire && (
|
||||||
<div className="flex items-start gap-4">
|
<div className="flex items-start gap-3 md:gap-4">
|
||||||
<div className="w-10 h-10 rounded-lg bg-gray-100 flex items-center justify-center flex-shrink-0">
|
<div className="w-8 h-8 md:w-10 md:h-10 rounded-lg bg-gray-100 flex items-center justify-center flex-shrink-0">
|
||||||
<svg className="w-5 h-5 text-gray-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
<svg className="w-4 h-4 md:w-5 md:h-5 text-gray-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M3 5a2 2 0 012-2h3.28a1 1 0 01.948.684l1.498 4.493a1 1 0 01-.502 1.21l-2.257 1.13a11.042 11.042 0 005.516 5.516l1.13-2.257a1 1 0 011.21-.502l4.493 1.498a1 1 0 01.684.949V19a2 2 0 01-2 2h-1C9.716 21 3 14.284 3 6V5z" />
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M3 5a2 2 0 012-2h3.28a1 1 0 01.948.684l1.498 4.493a1 1 0 01-.502 1.21l-2.257 1.13a11.042 11.042 0 005.516 5.516l1.13-2.257a1 1 0 011.21-.502l4.493 1.498a1 1 0 01.684.949V19a2 2 0 01-2 2h-1C9.716 21 3 14.284 3 6V5z" />
|
||||||
</svg>
|
</svg>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex-1 min-w-0">
|
<div className="flex-1 min-w-0">
|
||||||
<p className="text-xs font-medium text-gray-500 uppercase tracking-wide mb-1">Téléphone secondaire</p>
|
<p className="text-xs font-medium text-gray-500 uppercase tracking-wide mb-1">Téléphone secondaire</p>
|
||||||
<a href={`tel:${viewingAdherent.telephoneSecondaire}`} className="text-sm font-semibold text-lblue hover:text-dblue transition-colors">
|
<a href={`tel:${viewingAdherent.telephoneSecondaire}`} className="text-sm font-semibold text-lblue hover:text-dblue transition-colors break-all">
|
||||||
{viewingAdherent.telephoneSecondaire}
|
{viewingAdherent.telephoneSecondaire}
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<div className="flex items-start gap-4">
|
<div className="flex items-start gap-3 md:gap-4">
|
||||||
<div className="w-10 h-10 rounded-lg bg-gray-100 flex items-center justify-center flex-shrink-0">
|
<div className="w-8 h-8 md:w-10 md:h-10 rounded-lg bg-gray-100 flex items-center justify-center flex-shrink-0">
|
||||||
<svg className="w-5 h-5 text-gray-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
<svg className="w-4 h-4 md:w-5 md:h-5 text-gray-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M3 8l7.89 5.26a2 2 0 002.22 0L21 8M5 19h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v10a2 2 0 002 2z" />
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M3 8l7.89 5.26a2 2 0 002.22 0L21 8M5 19h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v10a2 2 0 002 2z" />
|
||||||
</svg>
|
</svg>
|
||||||
</div>
|
</div>
|
||||||
@@ -1269,38 +1381,38 @@ export default function AdherentsTable() {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="flex items-start gap-4">
|
<div className="flex items-start gap-3 md:gap-4">
|
||||||
<div className="w-10 h-10 rounded-lg bg-gray-100 flex items-center justify-center flex-shrink-0">
|
<div className="w-8 h-8 md:w-10 md:h-10 rounded-lg bg-gray-100 flex items-center justify-center flex-shrink-0">
|
||||||
<svg className="w-5 h-5 text-gray-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
<svg className="w-4 h-4 md:w-5 md:h-5 text-gray-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M17.657 16.657L13.414 20.9a1.998 1.998 0 01-2.827 0l-4.244-4.243a8 8 0 1111.314 0z" />
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M17.657 16.657L13.414 20.9a1.998 1.998 0 01-2.827 0l-4.244-4.243a8 8 0 1111.314 0z" />
|
||||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M15 11a3 3 0 11-6 0 3 3 0 016 0z" />
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M15 11a3 3 0 11-6 0 3 3 0 016 0z" />
|
||||||
</svg>
|
</svg>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex-1 min-w-0">
|
<div className="flex-1 min-w-0">
|
||||||
<p className="text-xs font-medium text-gray-500 uppercase tracking-wide mb-1">Adresse</p>
|
<p className="text-xs font-medium text-gray-500 uppercase tracking-wide mb-1">Adresse</p>
|
||||||
<p className="text-sm font-semibold text-gray-900">{viewingAdherent.adresse}</p>
|
<p className="text-sm font-semibold text-gray-900 break-words">{viewingAdherent.adresse}</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Carte Informations complémentaires */}
|
{/* Carte Informations complémentaires */}
|
||||||
<div className="bg-white rounded-xl border border-gray-200 p-6 shadow-sm">
|
<div className="bg-white rounded-lg md:rounded-xl border border-gray-200 p-4 md:p-6 shadow-sm">
|
||||||
<div className="flex items-center gap-3 mb-6 pb-4 border-b border-gray-200">
|
<div className="flex items-center gap-2 md:gap-3 mb-4 md:mb-6 pb-3 md:pb-4 border-b border-gray-200">
|
||||||
<div className="w-10 h-10 rounded-lg bg-lblue/10 flex items-center justify-center">
|
<div className="w-8 h-8 md:w-10 md:h-10 rounded-lg bg-lblue/10 flex items-center justify-center flex-shrink-0">
|
||||||
<svg className="w-5 h-5 text-lblue" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
<svg className="w-4 h-4 md:w-5 md:h-5 text-lblue" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z" />
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z" />
|
||||||
</svg>
|
</svg>
|
||||||
</div>
|
</div>
|
||||||
<h3 className="text-lg font-semibold text-gray-900">
|
<h3 className="text-base md:text-lg font-semibold text-gray-900">
|
||||||
Informations complémentaires
|
Informations complémentaires
|
||||||
</h3>
|
</h3>
|
||||||
</div>
|
</div>
|
||||||
<div className="space-y-4">
|
<div className="space-y-3 md:space-y-4">
|
||||||
{viewingAdherent.situation && (
|
{viewingAdherent.situation && (
|
||||||
<div className="flex items-start gap-4">
|
<div className="flex items-start gap-3 md:gap-4">
|
||||||
<div className="w-10 h-10 rounded-lg bg-gray-100 flex items-center justify-center flex-shrink-0">
|
<div className="w-8 h-8 md:w-10 md:h-10 rounded-lg bg-gray-100 flex items-center justify-center flex-shrink-0">
|
||||||
<svg className="w-5 h-5 text-gray-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
<svg className="w-4 h-4 md:w-5 md:h-5 text-gray-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z" />
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z" />
|
||||||
</svg>
|
</svg>
|
||||||
</div>
|
</div>
|
||||||
@@ -1312,9 +1424,9 @@ export default function AdherentsTable() {
|
|||||||
)}
|
)}
|
||||||
|
|
||||||
{viewingAdherent.prescripteur && (
|
{viewingAdherent.prescripteur && (
|
||||||
<div className="flex items-start gap-4">
|
<div className="flex items-start gap-3 md:gap-4">
|
||||||
<div className="w-10 h-10 rounded-lg bg-gray-100 flex items-center justify-center flex-shrink-0">
|
<div className="w-8 h-8 md:w-10 md:h-10 rounded-lg bg-gray-100 flex items-center justify-center flex-shrink-0">
|
||||||
<svg className="w-5 h-5 text-gray-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
<svg className="w-4 h-4 md:w-5 md:h-5 text-gray-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M16 7a4 4 0 11-8 0 4 4 0 018 0zM12 14a7 7 0 00-7 7h14a7 7 0 00-7-7z" />
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M16 7a4 4 0 11-8 0 4 4 0 018 0zM12 14a7 7 0 00-7 7h14a7 7 0 00-7-7z" />
|
||||||
</svg>
|
</svg>
|
||||||
</div>
|
</div>
|
||||||
@@ -1326,9 +1438,9 @@ export default function AdherentsTable() {
|
|||||||
)}
|
)}
|
||||||
|
|
||||||
{viewingAdherent.facturation && (
|
{viewingAdherent.facturation && (
|
||||||
<div className="flex items-start gap-4">
|
<div className="flex items-start gap-3 md:gap-4">
|
||||||
<div className="w-10 h-10 rounded-lg bg-gray-100 flex items-center justify-center flex-shrink-0">
|
<div className="w-8 h-8 md:w-10 md:h-10 rounded-lg bg-gray-100 flex items-center justify-center flex-shrink-0">
|
||||||
<svg className="w-5 h-5 text-gray-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
<svg className="w-4 h-4 md:w-5 md:h-5 text-gray-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9 14l6-6m-5.5.5h.01m4.99 5h.01M19 21V5a2 2 0 00-2-2H7a2 2 0 00-2 2v16l3.5-2 3.5 2 3.5-2 3.5 2zM10 8.5a.5.5 0 11-1 0 .5.5 0 011 0zm5 0a.5.5 0 11-1 0 .5.5 0 011 0z" />
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9 14l6-6m-5.5.5h.01m4.99 5h.01M19 21V5a2 2 0 00-2-2H7a2 2 0 00-2 2v16l3.5-2 3.5 2 3.5-2 3.5 2zM10 8.5a.5.5 0 11-1 0 .5.5 0 011 0zm5 0a.5.5 0 11-1 0 .5.5 0 011 0z" />
|
||||||
</svg>
|
</svg>
|
||||||
</div>
|
</div>
|
||||||
@@ -1340,9 +1452,9 @@ export default function AdherentsTable() {
|
|||||||
)}
|
)}
|
||||||
|
|
||||||
{viewingAdherent.forfait && (
|
{viewingAdherent.forfait && (
|
||||||
<div className="flex items-start gap-4">
|
<div className="flex items-start gap-3 md:gap-4">
|
||||||
<div className="w-10 h-10 rounded-lg bg-gray-100 flex items-center justify-center flex-shrink-0">
|
<div className="w-8 h-8 md:w-10 md:h-10 rounded-lg bg-gray-100 flex items-center justify-center flex-shrink-0">
|
||||||
<svg className="w-5 h-5 text-gray-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
<svg className="w-4 h-4 md:w-5 md:h-5 text-gray-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M12 8c-1.657 0-3 .895-3 2s1.343 2 3 2 3 .895 3 2-1.343 2-3 2m0-8c1.11 0 2.08.402 2.599 1M12 8V7m0 1v8m0 0v1m0-1c-1.11 0-2.08-.402-2.599-1M21 12a9 9 0 11-18 0 9 9 0 0118 0z" />
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M12 8c-1.657 0-3 .895-3 2s1.343 2 3 2 3 .895 3 2-1.343 2-3 2m0-8c1.11 0 2.08.402 2.599 1M12 8V7m0 1v8m0 0v1m0-1c-1.11 0-2.08-.402-2.599-1M21 12a9 9 0 11-18 0 9 9 0 0118 0z" />
|
||||||
</svg>
|
</svg>
|
||||||
</div>
|
</div>
|
||||||
@@ -1354,29 +1466,29 @@ export default function AdherentsTable() {
|
|||||||
)}
|
)}
|
||||||
|
|
||||||
{viewingAdherent.commentaire && (
|
{viewingAdherent.commentaire && (
|
||||||
<div className="flex items-start gap-4">
|
<div className="flex items-start gap-3 md:gap-4">
|
||||||
<div className="w-10 h-10 rounded-lg bg-gray-100 flex items-center justify-center flex-shrink-0">
|
<div className="w-8 h-8 md:w-10 md:h-10 rounded-lg bg-gray-100 flex items-center justify-center flex-shrink-0">
|
||||||
<svg className="w-5 h-5 text-gray-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
<svg className="w-4 h-4 md:w-5 md:h-5 text-gray-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M7 8h10M7 12h4m1 8l-4-4H5a2 2 0 01-2-2V6a2 2 0 012-2h14a2 2 0 012 2v8a2 2 0 01-2 2h-3l-4 4z" />
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M7 8h10M7 12h4m1 8l-4-4H5a2 2 0 01-2-2V6a2 2 0 012-2h14a2 2 0 012 2v8a2 2 0 01-2 2h-3l-4 4z" />
|
||||||
</svg>
|
</svg>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex-1 min-w-0">
|
<div className="flex-1 min-w-0">
|
||||||
<p className="text-xs font-medium text-gray-500 uppercase tracking-wide mb-1">Commentaire</p>
|
<p className="text-xs font-medium text-gray-500 uppercase tracking-wide mb-1">Commentaire</p>
|
||||||
<p className="text-sm font-semibold text-gray-900 whitespace-pre-wrap">{viewingAdherent.commentaire}</p>
|
<p className="text-sm font-semibold text-gray-900 whitespace-pre-wrap break-words">{viewingAdherent.commentaire}</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{viewingAdherent.instructions && (
|
{viewingAdherent.instructions && (
|
||||||
<div className="flex items-start gap-4">
|
<div className="flex items-start gap-3 md:gap-4">
|
||||||
<div className="w-10 h-10 rounded-lg bg-gray-100 flex items-center justify-center flex-shrink-0">
|
<div className="w-8 h-8 md:w-10 md:h-10 rounded-lg bg-gray-100 flex items-center justify-center flex-shrink-0">
|
||||||
<svg className="w-5 h-5 text-gray-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
<svg className="w-4 h-4 md:w-5 md:h-5 text-gray-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z" />
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z" />
|
||||||
</svg>
|
</svg>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex-1 min-w-0">
|
<div className="flex-1 min-w-0">
|
||||||
<p className="text-xs font-medium text-gray-500 uppercase tracking-wide mb-1">Instructions</p>
|
<p className="text-xs font-medium text-gray-500 uppercase tracking-wide mb-1">Instructions</p>
|
||||||
<p className="text-sm font-semibold text-gray-900 whitespace-pre-wrap">{viewingAdherent.instructions}</p>
|
<p className="text-sm font-semibold text-gray-900 whitespace-pre-wrap break-words">{viewingAdherent.instructions}</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
@@ -1393,11 +1505,11 @@ export default function AdherentsTable() {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Footer avec actions */}
|
{/* Footer avec actions */}
|
||||||
<div className="border-t border-gray-200 px-8 py-5 bg-white">
|
<div className="border-t border-gray-200 px-4 md:px-8 py-4 md:py-5 bg-white">
|
||||||
<div className="flex items-center justify-between">
|
<div className="flex flex-col-reverse md:flex-row items-stretch md:items-center justify-between gap-3 md:gap-0">
|
||||||
<button
|
<button
|
||||||
onClick={() => setViewingAdherent(null)}
|
onClick={() => setViewingAdherent(null)}
|
||||||
className="px-5 py-2.5 text-sm font-medium text-gray-700 bg-white border border-gray-300 rounded-lg hover:bg-gray-50 transition-colors"
|
className="px-4 md:px-5 py-2.5 text-sm font-medium text-gray-700 bg-white border border-gray-300 rounded-lg hover:bg-gray-50 transition-colors w-full md:w-auto"
|
||||||
>
|
>
|
||||||
Fermer
|
Fermer
|
||||||
</button>
|
</button>
|
||||||
@@ -1406,9 +1518,9 @@ export default function AdherentsTable() {
|
|||||||
setViewingAdherent(null);
|
setViewingAdherent(null);
|
||||||
handleEdit(viewingAdherent);
|
handleEdit(viewingAdherent);
|
||||||
}}
|
}}
|
||||||
className="px-6 py-2.5 bg-lblue text-white text-sm font-semibold rounded-lg hover:bg-dblue transition-colors flex items-center gap-2"
|
className="px-4 md:px-6 py-2.5 bg-lblue text-white text-sm font-semibold rounded-lg hover:bg-dblue transition-colors flex items-center justify-center gap-2 w-full md:w-auto"
|
||||||
>
|
>
|
||||||
<svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
<svg className="w-4 h-4 md:w-5 md:h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M11 5H6a2 2 0 00-2 2v11a2 2 0 002 2h11a2 2 0 002-2v-5m-1.414-9.414a2 2 0 112.828 2.828L11.828 15H9v-2.828l8.586-8.586z" />
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M11 5H6a2 2 0 00-2 2v11a2 2 0 002 2h11a2 2 0 002-2v-5m-1.414-9.414a2 2 0 112.828 2.828L11.828 15H9v-2.828l8.586-8.586z" />
|
||||||
</svg>
|
</svg>
|
||||||
Modifier l'adhérent
|
Modifier l'adhérent
|
||||||
|
|||||||
@@ -130,8 +130,8 @@ export default function ArchivesTrajets() {
|
|||||||
|
|
||||||
if (loading) {
|
if (loading) {
|
||||||
return (
|
return (
|
||||||
<div className="bg-white rounded-lg shadow-sm p-8">
|
<div className="bg-white rounded-lg shadow-sm p-4 md:p-8">
|
||||||
<div className="text-center text-gray-500">Chargement des archives...</div>
|
<div className="text-center text-sm md:text-base text-gray-500">Chargement des archives...</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -139,18 +139,18 @@ export default function ArchivesTrajets() {
|
|||||||
return (
|
return (
|
||||||
<div className="bg-white rounded-lg shadow-sm">
|
<div className="bg-white rounded-lg shadow-sm">
|
||||||
{/* Barre de recherche */}
|
{/* Barre de recherche */}
|
||||||
<div className="p-6 border-b border-gray-200">
|
<div className="p-3 md:p-4 lg:p-6 border-b border-gray-200">
|
||||||
<div className="flex items-center gap-4">
|
<div className="flex items-center gap-2 md:gap-4">
|
||||||
<div className="flex-1 relative">
|
<div className="flex-1 relative">
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
placeholder="Rechercher dans les archives..."
|
placeholder="Rechercher dans les archives..."
|
||||||
value={searchTerm}
|
value={searchTerm}
|
||||||
onChange={(e) => setSearchTerm(e.target.value)}
|
onChange={(e) => setSearchTerm(e.target.value)}
|
||||||
className="w-full px-4 py-2.5 pl-10 border border-gray-300 rounded-lg focus:ring-2 focus:ring-lblue focus:border-transparent"
|
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"
|
||||||
/>
|
/>
|
||||||
<svg
|
<svg
|
||||||
className="w-5 h-5 text-gray-400 absolute left-3 top-1/2 transform -translate-y-1/2"
|
className="w-4 h-4 md:w-5 md:h-5 text-gray-400 absolute left-2.5 md:left-3 top-1/2 transform -translate-y-1/2"
|
||||||
fill="none"
|
fill="none"
|
||||||
stroke="currentColor"
|
stroke="currentColor"
|
||||||
viewBox="0 0 24 24"
|
viewBox="0 0 24 24"
|
||||||
@@ -162,73 +162,73 @@ export default function ArchivesTrajets() {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Liste des trajets archivés */}
|
{/* Liste des trajets archivés */}
|
||||||
<div className="p-6">
|
<div className="p-3 md:p-4 lg:p-6">
|
||||||
{filteredTrajets.length === 0 ? (
|
{filteredTrajets.length === 0 ? (
|
||||||
<div className="text-center py-12 text-gray-500">
|
<div className="text-center py-8 md:py-12 text-sm md:text-base text-gray-500">
|
||||||
{searchTerm ? 'Aucun trajet trouvé' : 'Aucun trajet archivé'}
|
{searchTerm ? 'Aucun trajet trouvé' : 'Aucun trajet archivé'}
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<div className="space-y-4">
|
<div className="space-y-3 md:space-y-4">
|
||||||
{filteredTrajets.map((trajet) => (
|
{filteredTrajets.map((trajet) => (
|
||||||
<div
|
<div
|
||||||
key={trajet.id}
|
key={trajet.id}
|
||||||
className="border border-gray-200 rounded-lg p-4 hover:shadow-md transition-shadow"
|
className="border border-gray-200 rounded-lg p-3 md:p-4 hover:shadow-md transition-shadow"
|
||||||
>
|
>
|
||||||
<div className="flex items-start justify-between">
|
<div className="flex flex-col md:flex-row md:items-start md:justify-between gap-3 md:gap-4">
|
||||||
<div className="flex-1">
|
<div className="flex-1">
|
||||||
<div className="flex items-center gap-3 mb-3">
|
<div className="flex items-center gap-2 md:gap-3 mb-2 md:mb-3 flex-wrap">
|
||||||
<div className="w-10 h-10 rounded-full bg-lgreen flex items-center justify-center text-white font-semibold text-sm">
|
<div className="w-8 h-8 md:w-10 md:h-10 rounded-full bg-lgreen flex items-center justify-center text-white font-semibold text-xs md:text-sm flex-shrink-0">
|
||||||
{getInitials(trajet.adherent.nom, trajet.adherent.prenom)}
|
{getInitials(trajet.adherent.nom, trajet.adherent.prenom)}
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div className="flex-1 min-w-0">
|
||||||
<div className="font-semibold text-gray-900">
|
<div className="font-semibold text-sm md:text-base text-gray-900 truncate">
|
||||||
{trajet.adherent.prenom} {trajet.adherent.nom}
|
{trajet.adherent.prenom} {trajet.adherent.nom}
|
||||||
</div>
|
</div>
|
||||||
<div className="text-sm text-gray-500">
|
<div className="text-xs md:text-sm text-gray-500">
|
||||||
{formatDate(trajet.date)} à {formatTime(trajet.date)}
|
{formatDate(trajet.date)} à {formatTime(trajet.date)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<span className={`px-2 py-1 text-xs font-medium rounded border ${getStatutColor(trajet.statut)}`}>
|
<span className={`px-2 py-1 text-xs font-medium rounded border ${getStatutColor(trajet.statut)} flex-shrink-0`}>
|
||||||
{trajet.statut}
|
{trajet.statut}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-3 mb-3">
|
<div className="grid grid-cols-1 md:grid-cols-2 gap-2 md:gap-3 mb-2 md:mb-3">
|
||||||
<div className="flex items-start gap-2">
|
<div className="flex items-start gap-2">
|
||||||
<div className="w-6 h-6 rounded-full bg-lgreen flex items-center justify-center text-white text-xs font-bold mt-0.5 flex-shrink-0">
|
<div className="w-5 h-5 md:w-6 md:h-6 rounded-full bg-lgreen flex items-center justify-center text-white text-xs font-bold mt-0.5 flex-shrink-0">
|
||||||
A
|
A
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div className="min-w-0 flex-1">
|
||||||
<div className="text-xs text-gray-500">Départ</div>
|
<div className="text-xs text-gray-500">Départ</div>
|
||||||
<div className="text-sm text-gray-900">{trajet.adresseDepart}</div>
|
<div className="text-xs md:text-sm text-gray-900 break-words">{trajet.adresseDepart}</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-start gap-2">
|
<div className="flex items-start gap-2">
|
||||||
<div className="w-6 h-6 rounded-full bg-lblue flex items-center justify-center text-white text-xs font-bold mt-0.5 flex-shrink-0">
|
<div className="w-5 h-5 md:w-6 md:h-6 rounded-full bg-lblue flex items-center justify-center text-white text-xs font-bold mt-0.5 flex-shrink-0">
|
||||||
B
|
B
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div className="min-w-0 flex-1">
|
||||||
<div className="text-xs text-gray-500">Arrivée</div>
|
<div className="text-xs text-gray-500">Arrivée</div>
|
||||||
<div className="text-sm text-gray-900">{trajet.adresseArrivee}</div>
|
<div className="text-xs md:text-sm text-gray-900 break-words">{trajet.adresseArrivee}</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{trajet.chauffeur && (
|
{trajet.chauffeur && (
|
||||||
<div className="flex items-center gap-2 text-sm text-gray-600">
|
<div className="flex items-center gap-2 text-xs md:text-sm text-gray-600">
|
||||||
<svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
<svg className="w-3.5 h-3.5 md:w-4 md:h-4 flex-shrink-0" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9 17a2 2 0 11-4 0 2 2 0 014 0zM19 17a2 2 0 11-4 0 2 2 0 014 0z" />
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9 17a2 2 0 11-4 0 2 2 0 014 0zM19 17a2 2 0 11-4 0 2 2 0 014 0z" />
|
||||||
</svg>
|
</svg>
|
||||||
<span>Chauffeur: {trajet.chauffeur.prenom} {trajet.chauffeur.nom}</span>
|
<span className="truncate">Chauffeur: {trajet.chauffeur.prenom} {trajet.chauffeur.nom}</span>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<button
|
<button
|
||||||
onClick={() => handleRestoreClick(trajet.id)}
|
onClick={() => handleRestoreClick(trajet.id)}
|
||||||
className="ml-4 px-4 py-2 text-sm font-medium text-lgreen hover:text-dgreen transition-colors flex items-center gap-2"
|
className="md:ml-4 px-3 md:px-4 py-2 text-xs md:text-sm font-medium text-lgreen hover:text-dgreen transition-colors flex items-center justify-center gap-1.5 md:gap-2 w-full md:w-auto"
|
||||||
>
|
>
|
||||||
<svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
<svg className="w-3.5 h-3.5 md:w-4 md:h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15" />
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15" />
|
||||||
</svg>
|
</svg>
|
||||||
Restaurer
|
Restaurer
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ export default function CalendrierPageContent() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="grid grid-cols-1 lg:grid-cols-3 gap-8">
|
<div className="grid grid-cols-1 lg:grid-cols-3 gap-4 sm:gap-6 lg:gap-8">
|
||||||
{/* Calendrier - Prend 2 colonnes sur grand écran */}
|
{/* Calendrier - Prend 2 colonnes sur grand écran */}
|
||||||
<div className="lg:col-span-2">
|
<div className="lg:col-span-2">
|
||||||
<CalendrierTrajets refreshTrigger={refreshTrigger} />
|
<CalendrierTrajets refreshTrigger={refreshTrigger} />
|
||||||
|
|||||||
@@ -97,16 +97,16 @@ function DraggableTrajetEvent({ trajet, onClick }: { trajet: Trajet; onClick: (e
|
|||||||
{...listeners}
|
{...listeners}
|
||||||
{...attributes}
|
{...attributes}
|
||||||
onClick={(e) => onClick(e, trajet)}
|
onClick={(e) => onClick(e, trajet)}
|
||||||
className={`text-[10px] px-1.5 py-0.5 rounded border ${styleObj.bg} ${styleObj.text} ${styleObj.border} font-semibold truncate w-full text-left transition-all flex items-center gap-1 ${canDrag ? 'hover:shadow-md cursor-grab active:cursor-grabbing' : 'cursor-default opacity-75'} ${isDragging ? 'opacity-50' : ''}`}
|
className={`text-[9px] sm:text-[10px] px-1 sm:px-1.5 py-0.5 rounded border ${styleObj.bg} ${styleObj.text} ${styleObj.border} font-semibold truncate w-full text-left transition-all flex items-center gap-0.5 sm:gap-1 ${canDrag ? 'hover:shadow-md cursor-grab active:cursor-grabbing' : 'cursor-default opacity-75'} ${isDragging ? 'opacity-50' : ''}`}
|
||||||
title={`${trajet.adherent.prenom} ${trajet.adherent.nom} - ${trajet.chauffeur ? `${trajet.chauffeur.prenom} ${trajet.chauffeur.nom}` : 'Sans chauffeur'} - ${trajet.statut}`}
|
title={`${trajet.adherent.prenom} ${trajet.adherent.nom} - ${trajet.chauffeur ? `${trajet.chauffeur.prenom} ${trajet.chauffeur.nom}` : 'Sans chauffeur'} - ${trajet.statut}`}
|
||||||
>
|
>
|
||||||
<span className="font-bold">
|
<span className="font-bold text-[8px] sm:text-[9px]">
|
||||||
{trajet.chauffeur
|
{trajet.chauffeur
|
||||||
? `${trajet.chauffeur.prenom.charAt(0)}${trajet.chauffeur.nom.charAt(0)}`
|
? `${trajet.chauffeur.prenom.charAt(0)}${trajet.chauffeur.nom.charAt(0)}`
|
||||||
: '?'}
|
: '?'}
|
||||||
</span>
|
</span>
|
||||||
<span className="opacity-90">•</span>
|
<span className="opacity-90 hidden sm:inline">•</span>
|
||||||
<span className="opacity-90">{formatTime(trajet.date)}</span>
|
<span className="opacity-90 truncate">{formatTime(trajet.date)}</span>
|
||||||
</button>
|
</button>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -137,7 +137,7 @@ function DroppableDayCell({
|
|||||||
<button
|
<button
|
||||||
ref={setNodeRef}
|
ref={setNodeRef}
|
||||||
onClick={() => onDateClick(date)}
|
onClick={() => onDateClick(date)}
|
||||||
className={`h-24 p-2 rounded-lg border-2 transition-all flex flex-col ${
|
className={`h-16 sm:h-20 md:h-24 p-1 sm:p-1.5 md:p-2 rounded-lg border-2 transition-all flex flex-col ${
|
||||||
isSelected
|
isSelected
|
||||||
? 'border-lblue bg-lblue/10'
|
? 'border-lblue bg-lblue/10'
|
||||||
: isToday
|
: isToday
|
||||||
@@ -369,34 +369,34 @@ export default function CalendrierTrajets({ refreshTrigger }: CalendrierTrajetsP
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="bg-white rounded-lg shadow-sm p-6">
|
<div className="bg-white rounded-lg shadow-sm p-4 sm:p-6">
|
||||||
{/* En-tête du calendrier */}
|
{/* En-tête du calendrier */}
|
||||||
<div className="flex items-center justify-between mb-6">
|
<div className="flex flex-col sm:flex-row items-start sm:items-center justify-between gap-3 sm:gap-0 mb-4 sm:mb-6">
|
||||||
<h2 className="text-xl font-semibold text-gray-900">
|
<h2 className="text-lg sm:text-xl font-semibold text-gray-900 capitalize">
|
||||||
{currentDate.toLocaleDateString('fr-FR', { month: 'long', year: 'numeric' })}
|
{currentDate.toLocaleDateString('fr-FR', { month: 'long', year: 'numeric' })}
|
||||||
</h2>
|
</h2>
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2 w-full sm:w-auto">
|
||||||
<button
|
<button
|
||||||
onClick={goToPreviousMonth}
|
onClick={goToPreviousMonth}
|
||||||
className="p-2 rounded-lg hover:bg-gray-100 transition-colors"
|
className="p-1.5 sm:p-2 rounded-lg hover:bg-gray-100 transition-colors"
|
||||||
title="Mois précédent"
|
title="Mois précédent"
|
||||||
>
|
>
|
||||||
<svg className="w-5 h-5 text-gray-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
<svg className="w-4 h-4 sm:w-5 sm:h-5 text-gray-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M15 19l-7-7 7-7" />
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M15 19l-7-7 7-7" />
|
||||||
</svg>
|
</svg>
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
onClick={goToToday}
|
onClick={goToToday}
|
||||||
className="px-4 py-2 text-sm font-medium text-gray-700 bg-gray-100 rounded-lg hover:bg-gray-200 transition-colors"
|
className="px-3 sm:px-4 py-1.5 sm:py-2 text-xs sm:text-sm font-medium text-gray-700 bg-gray-100 rounded-lg hover:bg-gray-200 transition-colors flex-1 sm:flex-initial"
|
||||||
>
|
>
|
||||||
Aujourd'hui
|
Aujourd'hui
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
onClick={goToNextMonth}
|
onClick={goToNextMonth}
|
||||||
className="p-2 rounded-lg hover:bg-gray-100 transition-colors"
|
className="p-1.5 sm:p-2 rounded-lg hover:bg-gray-100 transition-colors"
|
||||||
title="Mois suivant"
|
title="Mois suivant"
|
||||||
>
|
>
|
||||||
<svg className="w-5 h-5 text-gray-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
<svg className="w-4 h-4 sm:w-5 sm:h-5 text-gray-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9 5l7 7-7 7" />
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9 5l7 7-7 7" />
|
||||||
</svg>
|
</svg>
|
||||||
</button>
|
</button>
|
||||||
@@ -404,15 +404,15 @@ export default function CalendrierTrajets({ refreshTrigger }: CalendrierTrajetsP
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{loading ? (
|
{loading ? (
|
||||||
<div className="text-center py-8 text-gray-500">Chargement...</div>
|
<div className="text-center py-6 sm:py-8 text-sm text-gray-500">Chargement...</div>
|
||||||
) : (
|
) : (
|
||||||
<>
|
<>
|
||||||
{/* Grille du calendrier avec drag and drop */}
|
{/* Grille du calendrier avec drag and drop */}
|
||||||
<DndContext onDragStart={handleDragStart} onDragEnd={handleDragEnd}>
|
<DndContext onDragStart={handleDragStart} onDragEnd={handleDragEnd}>
|
||||||
<div className="grid grid-cols-7 gap-2 mb-6">
|
<div className="grid grid-cols-7 gap-1 sm:gap-2 mb-4 sm:mb-6">
|
||||||
{/* En-têtes des jours */}
|
{/* En-têtes des jours */}
|
||||||
{days.map((day) => (
|
{days.map((day) => (
|
||||||
<div key={day} className="text-center text-sm font-semibold text-gray-600 py-2">
|
<div key={day} className="text-center text-[10px] sm:text-xs md:text-sm font-semibold text-gray-600 py-1 sm:py-2">
|
||||||
{day}
|
{day}
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
@@ -442,21 +442,21 @@ export default function CalendrierTrajets({ refreshTrigger }: CalendrierTrajetsP
|
|||||||
isSelected={isSelected}
|
isSelected={isSelected}
|
||||||
onDateClick={handleDateClick}
|
onDateClick={handleDateClick}
|
||||||
>
|
>
|
||||||
<div className="text-sm font-medium text-gray-900 mb-1">
|
<div className="text-xs sm:text-sm font-medium text-gray-900 mb-0.5 sm:mb-1">
|
||||||
{date.getDate()}
|
{date.getDate()}
|
||||||
</div>
|
</div>
|
||||||
{trajetsDuJour.length > 0 && (
|
{trajetsDuJour.length > 0 && (
|
||||||
<div className="space-y-1 flex-1 overflow-hidden">
|
<div className="space-y-0.5 sm:space-y-1 flex-1 overflow-hidden">
|
||||||
{trajetsDuJour.slice(0, 3).map((trajet) => (
|
{trajetsDuJour.slice(0, 2).map((trajet) => (
|
||||||
<DraggableTrajetEvent
|
<DraggableTrajetEvent
|
||||||
key={trajet.id}
|
key={trajet.id}
|
||||||
trajet={trajet}
|
trajet={trajet}
|
||||||
onClick={handleTrajetClick}
|
onClick={handleTrajetClick}
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
{trajetsDuJour.length > 3 && (
|
{trajetsDuJour.length > 2 && (
|
||||||
<div className="text-[10px] text-gray-500 font-semibold text-center pt-0.5">
|
<div className="text-[9px] sm:text-[10px] text-gray-500 font-semibold text-center pt-0.5">
|
||||||
+{trajetsDuJour.length - 3}
|
+{trajetsDuJour.length - 2}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
@@ -484,11 +484,11 @@ export default function CalendrierTrajets({ refreshTrigger }: CalendrierTrajetsP
|
|||||||
|
|
||||||
{/* Détails des trajets du jour sélectionné */}
|
{/* Détails des trajets du jour sélectionné */}
|
||||||
{selectedDate && selectedTrajets.length > 0 && (
|
{selectedDate && selectedTrajets.length > 0 && (
|
||||||
<div className="mt-6 pt-6 border-t border-gray-200">
|
<div className="mt-4 sm:mt-6 pt-4 sm:pt-6 border-t border-gray-200">
|
||||||
<h3 className="text-lg font-semibold text-gray-900 mb-4">
|
<h3 className="text-base sm:text-lg font-semibold text-gray-900 mb-3 sm:mb-4">
|
||||||
Trajets du {formatDate(selectedDate)}
|
Trajets du {formatDate(selectedDate)}
|
||||||
</h3>
|
</h3>
|
||||||
<div className="space-y-3">
|
<div className="space-y-2 sm:space-y-3">
|
||||||
{selectedTrajets.map((trajet) => {
|
{selectedTrajets.map((trajet) => {
|
||||||
const getStatutColor = (statut: string) => {
|
const getStatutColor = (statut: string) => {
|
||||||
switch (statut) {
|
switch (statut) {
|
||||||
@@ -509,55 +509,55 @@ export default function CalendrierTrajets({ refreshTrigger }: CalendrierTrajetsP
|
|||||||
<button
|
<button
|
||||||
key={trajet.id}
|
key={trajet.id}
|
||||||
onClick={() => setSelectedTrajet(trajet)}
|
onClick={() => setSelectedTrajet(trajet)}
|
||||||
className="w-full p-4 bg-gray-50 rounded-lg border border-gray-200 hover:border-gray-300 hover:shadow-sm transition-all text-left"
|
className="w-full p-3 sm:p-4 bg-gray-50 rounded-lg border border-gray-200 hover:border-gray-300 hover:shadow-sm transition-all text-left"
|
||||||
>
|
>
|
||||||
<div className="flex items-start justify-between">
|
<div className="flex items-start justify-between gap-2">
|
||||||
<div className="flex-1">
|
<div className="flex-1 min-w-0">
|
||||||
<div className="flex items-center gap-2 mb-2">
|
<div className="flex flex-wrap items-center gap-1.5 sm:gap-2 mb-2">
|
||||||
<span className="text-sm font-semibold text-gray-900">
|
<span className="text-xs sm:text-sm font-semibold text-gray-900">
|
||||||
{trajet.adherent.prenom} {trajet.adherent.nom}
|
{trajet.adherent.prenom} {trajet.adherent.nom}
|
||||||
</span>
|
</span>
|
||||||
<span className="px-2 py-0.5 text-xs font-medium rounded bg-lblue/10 text-lblue">
|
<span className="px-1.5 sm:px-2 py-0.5 text-[10px] sm:text-xs font-medium rounded bg-lblue/10 text-lblue">
|
||||||
{formatTime(trajet.date)}
|
{formatTime(trajet.date)}
|
||||||
</span>
|
</span>
|
||||||
<span
|
<span
|
||||||
className={`px-2 py-0.5 text-xs font-medium rounded border ${getStatutColor(trajet.statut)}`}
|
className={`px-1.5 sm:px-2 py-0.5 text-[10px] sm:text-xs font-medium rounded border ${getStatutColor(trajet.statut)}`}
|
||||||
>
|
>
|
||||||
{trajet.statut}
|
{trajet.statut}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div className="text-sm text-gray-600 mb-1">
|
<div className="text-xs sm:text-sm text-gray-600 mb-1 break-words">
|
||||||
<span className="font-medium">Départ:</span> {trajet.adresseDepart}
|
<span className="font-medium">Départ:</span> {trajet.adresseDepart}
|
||||||
</div>
|
</div>
|
||||||
<div className="text-sm text-gray-600 mb-2">
|
<div className="text-xs sm:text-sm text-gray-600 mb-2 break-words">
|
||||||
<span className="font-medium">Arrivée:</span> {trajet.adresseArrivee}
|
<span className="font-medium">Arrivée:</span> {trajet.adresseArrivee}
|
||||||
</div>
|
</div>
|
||||||
{trajet.chauffeur ? (
|
{trajet.chauffeur ? (
|
||||||
<div className="flex items-center gap-2 mt-2">
|
<div className="flex items-center gap-2 mt-2">
|
||||||
<svg className="w-4 h-4 text-gray-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
<svg className="w-3.5 h-3.5 sm:w-4 sm:h-4 text-gray-400 flex-shrink-0" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9 17a2 2 0 11-4 0 2 2 0 014 0zM19 17a2 2 0 11-4 0 2 2 0 014 0z" />
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9 17a2 2 0 11-4 0 2 2 0 014 0zM19 17a2 2 0 11-4 0 2 2 0 014 0z" />
|
||||||
</svg>
|
</svg>
|
||||||
<span className="text-sm font-medium text-gray-900">
|
<span className="text-xs sm:text-sm font-medium text-gray-900 truncate">
|
||||||
Chauffeur: {trajet.chauffeur.prenom} {trajet.chauffeur.nom}
|
Chauffeur: {trajet.chauffeur.prenom} {trajet.chauffeur.nom}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<div className="flex items-center gap-2 mt-2">
|
<div className="flex items-center gap-2 mt-2">
|
||||||
<svg className="w-4 h-4 text-orange-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
<svg className="w-3.5 h-3.5 sm:w-4 sm:h-4 text-orange-400 flex-shrink-0" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z" />
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z" />
|
||||||
</svg>
|
</svg>
|
||||||
<span className="text-sm text-orange-600 font-medium">
|
<span className="text-xs sm:text-sm text-orange-600 font-medium">
|
||||||
Aucun chauffeur assigné
|
Aucun chauffeur assigné
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
{trajet.commentaire && (
|
{trajet.commentaire && (
|
||||||
<div className="mt-2 text-sm text-gray-500 italic line-clamp-2">
|
<div className="mt-2 text-xs sm:text-sm text-gray-500 italic line-clamp-2 break-words">
|
||||||
{trajet.commentaire}
|
{trajet.commentaire}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
<svg className="w-5 h-5 text-gray-400 flex-shrink-0" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
<svg className="w-4 h-4 sm:w-5 sm:h-5 text-gray-400 flex-shrink-0 mt-0.5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9 5l7 7-7 7" />
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9 5l7 7-7 7" />
|
||||||
</svg>
|
</svg>
|
||||||
</div>
|
</div>
|
||||||
@@ -569,7 +569,7 @@ export default function CalendrierTrajets({ refreshTrigger }: CalendrierTrajetsP
|
|||||||
)}
|
)}
|
||||||
|
|
||||||
{selectedDate && selectedTrajets.length === 0 && (
|
{selectedDate && selectedTrajets.length === 0 && (
|
||||||
<div className="mt-6 pt-6 border-t border-gray-200 text-center text-gray-500">
|
<div className="mt-4 sm:mt-6 pt-4 sm:pt-6 border-t border-gray-200 text-center text-xs sm:text-sm text-gray-500">
|
||||||
Aucun trajet prévu pour le {formatDate(selectedDate)}
|
Aucun trajet prévu pour le {formatDate(selectedDate)}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -46,6 +46,7 @@ interface ChatWindowProps {
|
|||||||
conversation: Conversation;
|
conversation: Conversation;
|
||||||
onNewMessage: () => void;
|
onNewMessage: () => void;
|
||||||
onShowGroupSettings: () => void;
|
onShowGroupSettings: () => void;
|
||||||
|
onShowSidebar?: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
const fetcher = (url: string) => fetch(url).then((res) => res.json());
|
const fetcher = (url: string) => fetch(url).then((res) => res.json());
|
||||||
@@ -55,6 +56,7 @@ export default function ChatWindow({
|
|||||||
conversation,
|
conversation,
|
||||||
onNewMessage,
|
onNewMessage,
|
||||||
onShowGroupSettings,
|
onShowGroupSettings,
|
||||||
|
onShowSidebar,
|
||||||
}: ChatWindowProps) {
|
}: ChatWindowProps) {
|
||||||
const [message, setMessage] = useState('');
|
const [message, setMessage] = useState('');
|
||||||
const [files, setFiles] = useState<File[]>([]);
|
const [files, setFiles] = useState<File[]>([]);
|
||||||
@@ -288,10 +290,22 @@ export default function ChatWindow({
|
|||||||
return (
|
return (
|
||||||
<div className="flex flex-col h-full">
|
<div className="flex flex-col h-full">
|
||||||
{/* En-tête */}
|
{/* En-tête */}
|
||||||
<div className="p-4 border-b border-gray-200 flex items-center justify-between bg-white">
|
<div className="p-3 md:p-4 border-b border-gray-200 flex items-center justify-between bg-white">
|
||||||
<div className="flex items-center gap-3">
|
<div className="flex items-center gap-2 md:gap-3 flex-1 min-w-0">
|
||||||
<div className="w-10 h-10 rounded-full bg-gradient-to-br from-lblue to-dblue flex items-center justify-center">
|
{/* Bouton retour sur mobile */}
|
||||||
<span className="text-white font-semibold text-sm">
|
{onShowSidebar && (
|
||||||
|
<button
|
||||||
|
onClick={onShowSidebar}
|
||||||
|
className="p-2 rounded-lg hover:bg-gray-100 transition-colors md:hidden flex-shrink-0"
|
||||||
|
title="Retour aux conversations"
|
||||||
|
>
|
||||||
|
<svg className="w-5 h-5 text-gray-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M15 19l-7-7 7-7" />
|
||||||
|
</svg>
|
||||||
|
</button>
|
||||||
|
)}
|
||||||
|
<div className="w-8 h-8 md:w-10 md:h-10 rounded-full bg-gradient-to-br from-lblue to-dblue flex items-center justify-center flex-shrink-0">
|
||||||
|
<span className="text-white font-semibold text-xs md:text-sm">
|
||||||
{conversation.type === 'group'
|
{conversation.type === 'group'
|
||||||
? conversation.displayName.charAt(0).toUpperCase()
|
? conversation.displayName.charAt(0).toUpperCase()
|
||||||
: conversation.displayName
|
: conversation.displayName
|
||||||
@@ -302,8 +316,8 @@ export default function ChatWindow({
|
|||||||
.slice(0, 2)}
|
.slice(0, 2)}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div className="flex-1 min-w-0">
|
||||||
<h3 className="font-semibold text-gray-900">{conversation.displayName}</h3>
|
<h3 className="font-semibold text-gray-900 text-sm md:text-base truncate">{conversation.displayName}</h3>
|
||||||
{conversation.type === 'group' && (
|
{conversation.type === 'group' && (
|
||||||
<p className="text-xs text-gray-500">
|
<p className="text-xs text-gray-500">
|
||||||
{conversation.participants.length} participant{conversation.participants.length > 1 ? 's' : ''}
|
{conversation.participants.length} participant{conversation.participants.length > 1 ? 's' : ''}
|
||||||
@@ -314,7 +328,7 @@ export default function ChatWindow({
|
|||||||
{conversation.type === 'group' && (
|
{conversation.type === 'group' && (
|
||||||
<button
|
<button
|
||||||
onClick={onShowGroupSettings}
|
onClick={onShowGroupSettings}
|
||||||
className="p-2 rounded-lg hover:bg-gray-100 transition-colors"
|
className="p-2 rounded-lg hover:bg-gray-100 transition-colors flex-shrink-0"
|
||||||
title="Paramètres du groupe"
|
title="Paramètres du groupe"
|
||||||
>
|
>
|
||||||
<svg className="w-5 h-5 text-gray-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
<svg className="w-5 h-5 text-gray-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
@@ -325,7 +339,7 @@ export default function ChatWindow({
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Messages */}
|
{/* Messages */}
|
||||||
<div className="flex-1 overflow-y-auto p-4 bg-gray-50">
|
<div className="flex-1 overflow-y-auto p-2 md:p-4 bg-gray-50">
|
||||||
{error && (
|
{error && (
|
||||||
<div className="text-center text-red-600 py-4">
|
<div className="text-center text-red-600 py-4">
|
||||||
Erreur lors du chargement des messages
|
Erreur lors du chargement des messages
|
||||||
@@ -348,7 +362,7 @@ export default function ChatWindow({
|
|||||||
className={`flex ${isOwnMessage ? 'justify-end' : 'justify-start'}`}
|
className={`flex ${isOwnMessage ? 'justify-end' : 'justify-start'}`}
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
className={`max-w-[70%] rounded-lg p-3 ${
|
className={`max-w-[85%] md:max-w-[70%] rounded-lg p-2 md:p-3 ${
|
||||||
isOwnMessage
|
isOwnMessage
|
||||||
? 'bg-lblue text-white'
|
? 'bg-lblue text-white'
|
||||||
: 'bg-white text-gray-900 border border-gray-200'
|
: 'bg-white text-gray-900 border border-gray-200'
|
||||||
@@ -507,19 +521,19 @@ export default function ChatWindow({
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Zone de saisie */}
|
{/* Zone de saisie */}
|
||||||
<div className="p-4 border-t border-gray-200 bg-white">
|
<div className="p-2 md:p-4 border-t border-gray-200 bg-white">
|
||||||
{/* Fichiers sélectionnés */}
|
{/* Fichiers sélectionnés */}
|
||||||
{files.length > 0 && (
|
{files.length > 0 && (
|
||||||
<div className="mb-2 flex flex-wrap gap-2">
|
<div className="mb-2 flex flex-wrap gap-2">
|
||||||
{files.map((file, index) => (
|
{files.map((file, index) => (
|
||||||
<div
|
<div
|
||||||
key={index}
|
key={index}
|
||||||
className="flex items-center gap-2 bg-gray-100 rounded-lg px-3 py-2 text-sm"
|
className="flex items-center gap-2 bg-gray-100 rounded-lg px-2 md:px-3 py-1 md:py-2 text-xs md:text-sm"
|
||||||
>
|
>
|
||||||
<span className="text-gray-700">{file.name}</span>
|
<span className="text-gray-700 truncate max-w-[150px] md:max-w-none">{file.name}</span>
|
||||||
<button
|
<button
|
||||||
onClick={() => removeFile(index)}
|
onClick={() => removeFile(index)}
|
||||||
className="text-red-600 hover:text-red-700"
|
className="text-red-600 hover:text-red-700 flex-shrink-0"
|
||||||
>
|
>
|
||||||
<svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
<svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
<path
|
<path
|
||||||
@@ -535,7 +549,7 @@ export default function ChatWindow({
|
|||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<div className="flex items-end gap-2">
|
<div className="flex items-end gap-1 md:gap-2">
|
||||||
<input
|
<input
|
||||||
type="file"
|
type="file"
|
||||||
ref={fileInputRef}
|
ref={fileInputRef}
|
||||||
@@ -546,10 +560,10 @@ export default function ChatWindow({
|
|||||||
/>
|
/>
|
||||||
<label
|
<label
|
||||||
htmlFor="file-input"
|
htmlFor="file-input"
|
||||||
className="p-2 rounded-lg hover:bg-gray-100 transition-colors cursor-pointer"
|
className="p-1.5 md:p-2 rounded-lg hover:bg-gray-100 transition-colors cursor-pointer flex-shrink-0"
|
||||||
title="Joindre un fichier"
|
title="Joindre un fichier"
|
||||||
>
|
>
|
||||||
<svg className="w-5 h-5 text-gray-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
<svg className="w-4 h-4 md:w-5 md:h-5 text-gray-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
<path
|
<path
|
||||||
strokeLinecap="round"
|
strokeLinecap="round"
|
||||||
strokeLinejoin="round"
|
strokeLinejoin="round"
|
||||||
@@ -563,17 +577,17 @@ export default function ChatWindow({
|
|||||||
onChange={handleMessageChange}
|
onChange={handleMessageChange}
|
||||||
onKeyPress={handleKeyPress}
|
onKeyPress={handleKeyPress}
|
||||||
placeholder="Tapez votre message..."
|
placeholder="Tapez votre message..."
|
||||||
className="flex-1 min-h-[44px] max-h-32 px-4 py-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-lblue focus:border-transparent resize-none text-gray-900 placeholder-gray-400"
|
className="flex-1 min-h-[40px] md:min-h-[44px] max-h-32 px-3 md:px-4 py-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-lblue focus:border-transparent resize-none text-sm md:text-base text-gray-900 placeholder-gray-400"
|
||||||
rows={1}
|
rows={1}
|
||||||
/>
|
/>
|
||||||
<button
|
<button
|
||||||
onClick={handleSendMessage}
|
onClick={handleSendMessage}
|
||||||
disabled={(!message.trim() && files.length === 0) || isSending}
|
disabled={(!message.trim() && files.length === 0) || isSending}
|
||||||
className="p-2 rounded-lg bg-lblue text-white hover:bg-dblue transition-colors disabled:opacity-50 disabled:cursor-not-allowed"
|
className="p-1.5 md:p-2 rounded-lg bg-lblue text-white hover:bg-dblue transition-colors disabled:opacity-50 disabled:cursor-not-allowed flex-shrink-0"
|
||||||
title="Envoyer"
|
title="Envoyer"
|
||||||
>
|
>
|
||||||
{isSending ? (
|
{isSending ? (
|
||||||
<svg className="w-5 h-5 animate-spin" fill="none" viewBox="0 0 24 24">
|
<svg className="w-4 h-4 md:w-5 md:h-5 animate-spin" fill="none" viewBox="0 0 24 24">
|
||||||
<circle
|
<circle
|
||||||
className="opacity-25"
|
className="opacity-25"
|
||||||
cx="12"
|
cx="12"
|
||||||
@@ -589,7 +603,7 @@ export default function ChatWindow({
|
|||||||
/>
|
/>
|
||||||
</svg>
|
</svg>
|
||||||
) : (
|
) : (
|
||||||
<svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
<svg className="w-4 h-4 md:w-5 md:h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
<path
|
<path
|
||||||
strokeLinecap="round"
|
strokeLinecap="round"
|
||||||
strokeLinejoin="round"
|
strokeLinejoin="round"
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ interface ChauffeurFormProps {
|
|||||||
|
|
||||||
export default function ChauffeurForm({ chauffeur, onClose }: ChauffeurFormProps) {
|
export default function ChauffeurForm({ chauffeur, onClose }: ChauffeurFormProps) {
|
||||||
const [loading, setLoading] = useState(false);
|
const [loading, setLoading] = useState(false);
|
||||||
|
const [isMobile, setIsMobile] = useState(false);
|
||||||
const [alertModal, setAlertModal] = useState<{
|
const [alertModal, setAlertModal] = useState<{
|
||||||
show: boolean;
|
show: boolean;
|
||||||
type: 'success' | 'error' | 'info' | 'warning';
|
type: 'success' | 'error' | 'info' | 'warning';
|
||||||
@@ -44,6 +45,17 @@ export default function ChauffeurForm({ chauffeur, onClose }: ChauffeurFormProps
|
|||||||
status: 'Disponible',
|
status: 'Disponible',
|
||||||
});
|
});
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const checkMobile = () => {
|
||||||
|
setIsMobile(window.innerWidth < 768); // md breakpoint
|
||||||
|
};
|
||||||
|
|
||||||
|
checkMobile();
|
||||||
|
window.addEventListener('resize', checkMobile);
|
||||||
|
|
||||||
|
return () => window.removeEventListener('resize', checkMobile);
|
||||||
|
}, []);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (chauffeur) {
|
if (chauffeur) {
|
||||||
const dateNaissance = new Date(chauffeur.dateNaissance);
|
const dateNaissance = new Date(chauffeur.dateNaissance);
|
||||||
@@ -110,6 +122,51 @@ export default function ChauffeurForm({ chauffeur, onClose }: ChauffeurFormProps
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Si on est sur mobile, afficher un message au lieu du formulaire
|
||||||
|
if (isMobile) {
|
||||||
|
return (
|
||||||
|
<div className="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50 p-4">
|
||||||
|
<div className="bg-white rounded-lg p-6 max-w-md w-full mx-4">
|
||||||
|
<div className="flex justify-between items-start mb-6">
|
||||||
|
<div className="flex-1">
|
||||||
|
<h2 className="text-xl font-semibold text-gray-900 mb-2">
|
||||||
|
{chauffeur ? 'Modifier le chauffeur' : 'Nouveau chauffeur'}
|
||||||
|
</h2>
|
||||||
|
</div>
|
||||||
|
<button
|
||||||
|
onClick={onClose}
|
||||||
|
className="text-gray-400 hover:text-gray-600 ml-4"
|
||||||
|
>
|
||||||
|
<svg className="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M6 18L18 6M6 6l12 12" />
|
||||||
|
</svg>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="text-center py-8">
|
||||||
|
<div className="mb-6">
|
||||||
|
<svg className="w-16 h-16 mx-auto text-gray-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9.75 17L9 20l-1 1h8l-1-1-.75-3M3 13h18M5 17h14a2 2 0 002-2V5a2 2 0 00-2-2H5a2 2 0 00-2 2v10a2 2 0 002 2z" />
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
<h3 className="text-lg font-semibold text-gray-900 mb-3">
|
||||||
|
Utilisez un ordinateur
|
||||||
|
</h3>
|
||||||
|
<p className="text-sm text-gray-600 mb-6">
|
||||||
|
Pour {chauffeur ? 'modifier' : 'créer'} un chauffeur, veuillez utiliser un ordinateur. Cette fonctionnalité n'est pas optimisée pour les appareils mobiles.
|
||||||
|
</p>
|
||||||
|
<button
|
||||||
|
onClick={onClose}
|
||||||
|
className="px-6 py-2 bg-lblue text-white rounded-lg hover:bg-dblue transition-colors"
|
||||||
|
>
|
||||||
|
Fermer
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50">
|
<div className="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50">
|
||||||
<div className="bg-white rounded-lg p-6 max-w-2xl w-full mx-4 max-h-[90vh] overflow-y-auto">
|
<div className="bg-white rounded-lg p-6 max-w-2xl w-full mx-4 max-h-[90vh] overflow-y-auto">
|
||||||
|
|||||||
@@ -617,50 +617,54 @@ export default function ChauffeursTable() {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Boutons d'action */}
|
{/* Boutons d'action */}
|
||||||
<div className="flex gap-3">
|
<div className="flex gap-2 md:gap-3 w-full md:w-auto">
|
||||||
<button
|
<button
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setEditingChauffeur(null);
|
setEditingChauffeur(null);
|
||||||
setShowForm(true);
|
setShowForm(true);
|
||||||
}}
|
}}
|
||||||
className="flex items-center gap-2 px-4 py-2 bg-lgreen text-white rounded-lg hover:bg-dgreen transition-colors"
|
className="flex items-center gap-1 md:gap-2 px-3 md:px-4 py-2 bg-lgreen text-white rounded-lg hover:bg-dgreen transition-colors text-sm md:text-base flex-1 md:flex-none justify-center"
|
||||||
>
|
>
|
||||||
<svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
<svg className="w-4 h-4 md:w-5 md:h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M12 4v16m8-8H4" />
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M12 4v16m8-8H4" />
|
||||||
</svg>
|
</svg>
|
||||||
Nouveau chauffeur
|
<span className="hidden sm:inline">Nouveau chauffeur</span>
|
||||||
|
<span className="sm:hidden">Nouveau</span>
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
onClick={() => setShowImportModal(true)}
|
onClick={() => setShowImportModal(true)}
|
||||||
className="flex items-center gap-2 px-4 py-2 bg-lblue text-white rounded-lg hover:bg-dblue transition-colors"
|
className="flex items-center gap-1 md:gap-2 px-3 md:px-4 py-2 bg-lblue text-white rounded-lg hover:bg-dblue transition-colors text-sm md:text-base"
|
||||||
>
|
>
|
||||||
<svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
<svg className="w-4 h-4 md:w-5 md:h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M4 16v1a3 3 0 003 3h10a3 3 0 003-3v-1m-4-4l-4 4m0 0l-4-4m4 4V4" />
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M4 16v1a3 3 0 003 3h10a3 3 0 003-3v-1m-4-4l-4 4m0 0l-4-4m4 4V4" />
|
||||||
</svg>
|
</svg>
|
||||||
Importer
|
<span className="hidden sm:inline">Importer</span>
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
onClick={handleExport}
|
onClick={handleExport}
|
||||||
disabled={selectedIds.size === 0}
|
disabled={selectedIds.size === 0}
|
||||||
className="flex items-center gap-2 px-4 py-2 bg-lorange text-white rounded-lg hover:bg-dorange transition-colors disabled:opacity-50 disabled:cursor-not-allowed"
|
className="flex items-center gap-1 md:gap-2 px-3 md:px-4 py-2 bg-lorange text-white rounded-lg hover:bg-dorange transition-colors disabled:opacity-50 disabled:cursor-not-allowed text-sm md:text-base"
|
||||||
>
|
>
|
||||||
<svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
<svg className="w-4 h-4 md:w-5 md:h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M4 16v1a3 3 0 003 3h10a3 3 0 003-3v-1m-4-4l-4-4m0 0l-4-4m4 4V4" />
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M4 16v1a3 3 0 003 3h10a3 3 0 003-3v-1m-4-4l-4-4m0 0l-4-4m4 4V4" />
|
||||||
</svg>
|
</svg>
|
||||||
Exporter {selectedIds.size > 0 && `(${selectedIds.size})`}
|
<span className="hidden sm:inline">Exporter</span>
|
||||||
|
{selectedIds.size > 0 && <span className="ml-1">({selectedIds.size})</span>}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Tableau */}
|
{/* Tableau - Desktop */}
|
||||||
<div className="bg-white rounded-lg shadow-sm overflow-hidden">
|
<div className="bg-white rounded-lg shadow-sm overflow-hidden">
|
||||||
{loading ? (
|
{loading ? (
|
||||||
<div className="p-8 text-center text-gray-500">Chargement...</div>
|
<div className="p-8 text-center text-gray-500">Chargement...</div>
|
||||||
) : chauffeurs.length === 0 ? (
|
) : chauffeurs.length === 0 ? (
|
||||||
<div className="p-8 text-center text-gray-500">Aucun chauffeur trouvé</div>
|
<div className="p-8 text-center text-gray-500">Aucun chauffeur trouvé</div>
|
||||||
) : (
|
) : (
|
||||||
<div className="overflow-x-auto">
|
<>
|
||||||
|
{/* Vue desktop - Tableau */}
|
||||||
|
<div className="hidden md:block overflow-x-auto">
|
||||||
<table className="min-w-full divide-y divide-gray-200">
|
<table className="min-w-full divide-y divide-gray-200">
|
||||||
<thead className="bg-gray-50">
|
<thead className="bg-gray-50">
|
||||||
<tr>
|
<tr>
|
||||||
@@ -772,6 +776,117 @@ export default function ChauffeursTable() {
|
|||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{/* Vue mobile - Cartes */}
|
||||||
|
<div className="md:hidden divide-y divide-gray-200">
|
||||||
|
{chauffeurs.map((chauffeur) => (
|
||||||
|
<div key={chauffeur.id} className="p-4 hover:bg-gray-50">
|
||||||
|
<div className="flex items-start gap-3">
|
||||||
|
{/* Checkbox */}
|
||||||
|
<input
|
||||||
|
type="checkbox"
|
||||||
|
checked={selectedIds.has(chauffeur.id)}
|
||||||
|
onChange={(e) => handleSelectOne(chauffeur.id, e.target.checked)}
|
||||||
|
className="w-4 h-4 text-lblue border-gray-300 rounded focus:ring-lblue mt-1"
|
||||||
|
/>
|
||||||
|
|
||||||
|
{/* Avatar */}
|
||||||
|
<div className="w-12 h-12 rounded-full bg-lorange flex items-center justify-center text-white font-semibold flex-shrink-0">
|
||||||
|
{getInitials(chauffeur.nom, chauffeur.prenom)}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Contenu principal */}
|
||||||
|
<div className="flex-1 min-w-0">
|
||||||
|
{/* Nom et date de naissance */}
|
||||||
|
<div className="mb-2">
|
||||||
|
<div className="text-base font-semibold text-gray-900">
|
||||||
|
{chauffeur.prenom} {chauffeur.nom}
|
||||||
|
</div>
|
||||||
|
<div className="text-xs text-gray-500">
|
||||||
|
Né le {formatDate(chauffeur.dateNaissance)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Contact */}
|
||||||
|
<div className="mb-2">
|
||||||
|
<div className="text-xs font-medium text-gray-500 uppercase tracking-wide mb-1">Contact</div>
|
||||||
|
<a href={`tel:${chauffeur.telephone}`} className="text-sm text-gray-900 block truncate">
|
||||||
|
{chauffeur.telephone}
|
||||||
|
</a>
|
||||||
|
<a href={`mailto:${chauffeur.email}`} className="text-sm text-gray-500 block truncate">
|
||||||
|
{chauffeur.email}
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Adresse */}
|
||||||
|
<div className="mb-2">
|
||||||
|
<div className="text-xs font-medium text-gray-500 uppercase tracking-wide mb-1">Adresse</div>
|
||||||
|
<div className="text-sm text-gray-900 line-clamp-2">
|
||||||
|
{chauffeur.adresse}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Heures */}
|
||||||
|
<div className="mb-2">
|
||||||
|
<div className="flex items-center justify-between mb-1">
|
||||||
|
<span className="text-xs text-gray-600">
|
||||||
|
{chauffeur.heuresRestantes || chauffeur.heuresContrat}h restantes sur {chauffeur.heuresContrat}h
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div className="w-full bg-gray-200 rounded-full h-2">
|
||||||
|
<div
|
||||||
|
className="bg-lgreen h-2 rounded-full"
|
||||||
|
style={{ width: `${getProgressPercentage(chauffeur.heuresRestantes || chauffeur.heuresContrat, chauffeur.heuresContrat)}%` }}
|
||||||
|
></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Status */}
|
||||||
|
{chauffeur.status && (
|
||||||
|
<div className="mb-3">
|
||||||
|
<span className={`px-2 py-1 inline-flex text-xs leading-4 font-semibold rounded-full ${getStatusColor(chauffeur.status)}`}>
|
||||||
|
{chauffeur.status}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{/* Actions */}
|
||||||
|
<div className="flex items-center gap-4 pt-2 border-t border-gray-200">
|
||||||
|
<button
|
||||||
|
onClick={() => handleView(chauffeur.id)}
|
||||||
|
className="flex items-center gap-1 text-lblue hover:text-dblue text-sm"
|
||||||
|
>
|
||||||
|
<svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M15 12a3 3 0 11-6 0 3 3 0 016 0z" />
|
||||||
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M2.458 12C3.732 7.943 7.523 5 12 5c4.478 0 8.268 2.943 9.542 7-1.274 4.057-5.064 7-9.542 7-4.477 0-8.268-2.943-9.542-7z" />
|
||||||
|
</svg>
|
||||||
|
Voir
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
onClick={() => handleEdit(chauffeur)}
|
||||||
|
className="flex items-center gap-1 text-lblue hover:text-dblue text-sm"
|
||||||
|
>
|
||||||
|
<svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M11 5H6a2 2 0 00-2 2v11a2 2 0 002 2h11a2 2 0 002-2v-5m-1.414-9.414a2 2 0 112.828 2.828L11.828 15H9v-2.828l8.586-8.586z" />
|
||||||
|
</svg>
|
||||||
|
Modifier
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
onClick={() => handleDelete(chauffeur.id)}
|
||||||
|
className="flex items-center gap-1 text-red-500 hover:text-red-700 text-sm ml-auto"
|
||||||
|
>
|
||||||
|
<svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16" />
|
||||||
|
</svg>
|
||||||
|
Supprimer
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -1020,25 +1135,25 @@ export default function ChauffeursTable() {
|
|||||||
|
|
||||||
{/* Modal vue détaillée - Design épuré */}
|
{/* Modal vue détaillée - Design épuré */}
|
||||||
{viewingChauffeur && (
|
{viewingChauffeur && (
|
||||||
<div className="fixed inset-0 bg-black/60 backdrop-blur-sm flex items-center justify-center z-50 p-4 animate-fadeIn">
|
<div className="fixed inset-0 bg-black/60 backdrop-blur-sm flex items-center justify-center z-50 p-0 md:p-4 animate-fadeIn">
|
||||||
<div className="bg-white rounded-xl shadow-2xl max-w-5xl w-full max-h-[95vh] overflow-hidden flex flex-col animate-slideUp border border-gray-200">
|
<div className="bg-white rounded-none md:rounded-xl shadow-2xl max-w-5xl w-full h-full md:h-auto md:max-h-[95vh] overflow-hidden flex flex-col animate-slideUp border-0 md:border border-gray-200">
|
||||||
{/* Header épuré */}
|
{/* Header épuré */}
|
||||||
<div className="border-b border-gray-200 px-8 py-6 bg-white">
|
<div className="border-b border-gray-200 px-4 md:px-8 py-4 md:py-6 bg-white">
|
||||||
<div className="flex items-start justify-between">
|
<div className="flex items-start justify-between">
|
||||||
<div className="flex items-center gap-5">
|
<div className="flex items-center gap-3 md:gap-5 flex-1 min-w-0">
|
||||||
<div className="w-16 h-16 rounded-full bg-lblue flex items-center justify-center text-white font-bold text-xl border-4 border-lblue/10 shadow-sm">
|
<div className="w-12 h-12 md:w-16 md:h-16 rounded-full bg-lblue flex items-center justify-center text-white font-bold text-lg md:text-xl border-2 md:border-4 border-lblue/10 shadow-sm flex-shrink-0">
|
||||||
{getInitials(viewingChauffeur.nom, viewingChauffeur.prenom)}
|
{getInitials(viewingChauffeur.nom, viewingChauffeur.prenom)}
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div className="flex-1 min-w-0">
|
||||||
<h2 className="text-2xl font-bold text-gray-900 mb-1">
|
<h2 className="text-xl md:text-2xl font-bold text-gray-900 mb-1 truncate">
|
||||||
{viewingChauffeur.prenom} {viewingChauffeur.nom}
|
{viewingChauffeur.prenom} {viewingChauffeur.nom}
|
||||||
</h2>
|
</h2>
|
||||||
<p className="text-gray-500 text-sm mb-2">
|
<p className="text-gray-500 text-xs md:text-sm mb-2">
|
||||||
Informations détaillées du chauffeur
|
Informations détaillées du chauffeur
|
||||||
</p>
|
</p>
|
||||||
{viewingChauffeur.status && (
|
{viewingChauffeur.status && (
|
||||||
<div>
|
<div>
|
||||||
<span className={`px-3 py-1 text-xs font-semibold rounded-full ${getStatusColor(viewingChauffeur.status)}`}>
|
<span className={`px-2 md:px-3 py-1 text-xs font-semibold rounded-full ${getStatusColor(viewingChauffeur.status)}`}>
|
||||||
{viewingChauffeur.status}
|
{viewingChauffeur.status}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
@@ -1047,9 +1162,9 @@ export default function ChauffeursTable() {
|
|||||||
</div>
|
</div>
|
||||||
<button
|
<button
|
||||||
onClick={() => setViewingChauffeur(null)}
|
onClick={() => setViewingChauffeur(null)}
|
||||||
className="text-gray-400 hover:text-gray-600 transition-colors p-2 hover:bg-gray-100 rounded-lg"
|
className="text-gray-400 hover:text-gray-600 transition-colors p-2 hover:bg-gray-100 rounded-lg flex-shrink-0 ml-2"
|
||||||
>
|
>
|
||||||
<svg className="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
<svg className="w-5 h-5 md:w-6 md:h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M6 18L18 6M6 6l12 12" />
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M6 18L18 6M6 6l12 12" />
|
||||||
</svg>
|
</svg>
|
||||||
</button>
|
</button>
|
||||||
@@ -1058,46 +1173,46 @@ export default function ChauffeursTable() {
|
|||||||
|
|
||||||
{/* Contenu scrollable */}
|
{/* Contenu scrollable */}
|
||||||
<div className="flex-1 overflow-y-auto">
|
<div className="flex-1 overflow-y-auto">
|
||||||
<div className="p-8">
|
<div className="p-4 md:p-8">
|
||||||
{/* Actions rapides */}
|
{/* Actions rapides */}
|
||||||
<div className="mb-8 flex flex-wrap gap-3">
|
<div className="mb-6 md:mb-8 flex flex-wrap gap-2 md:gap-3">
|
||||||
<a
|
<a
|
||||||
href={`tel:${viewingChauffeur.telephone}`}
|
href={`tel:${viewingChauffeur.telephone}`}
|
||||||
className="flex items-center gap-2 px-4 py-2 bg-gray-50 text-gray-700 border border-gray-200 rounded-lg hover:bg-gray-100 hover:border-lblue transition-colors"
|
className="flex items-center gap-2 px-3 md:px-4 py-2 bg-gray-50 text-gray-700 border border-gray-200 rounded-lg hover:bg-gray-100 hover:border-lblue transition-colors flex-1 md:flex-none justify-center"
|
||||||
>
|
>
|
||||||
<svg className="w-5 h-5 text-lblue" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
<svg className="w-4 h-4 md:w-5 md:h-5 text-lblue" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M3 5a2 2 0 012-2h3.28a1 1 0 01.948.684l1.498 4.493a1 1 0 01-.502 1.21l-2.257 1.13a11.042 11.042 0 005.516 5.516l1.13-2.257a1 1 0 011.21-.502l4.493 1.498a1 1 0 01.684.949V19a2 2 0 01-2 2h-1C9.716 21 3 14.284 3 6V5z" />
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M3 5a2 2 0 012-2h3.28a1 1 0 01.948.684l1.498 4.493a1 1 0 01-.502 1.21l-2.257 1.13a11.042 11.042 0 005.516 5.516l1.13-2.257a1 1 0 011.21-.502l4.493 1.498a1 1 0 01.684.949V19a2 2 0 01-2 2h-1C9.716 21 3 14.284 3 6V5z" />
|
||||||
</svg>
|
</svg>
|
||||||
<span className="text-sm font-medium">Appeler</span>
|
<span className="text-xs md:text-sm font-medium">Appeler</span>
|
||||||
</a>
|
</a>
|
||||||
<a
|
<a
|
||||||
href={`mailto:${viewingChauffeur.email}`}
|
href={`mailto:${viewingChauffeur.email}`}
|
||||||
className="flex items-center gap-2 px-4 py-2 bg-gray-50 text-gray-700 border border-gray-200 rounded-lg hover:bg-gray-100 hover:border-lblue transition-colors"
|
className="flex items-center gap-2 px-3 md:px-4 py-2 bg-gray-50 text-gray-700 border border-gray-200 rounded-lg hover:bg-gray-100 hover:border-lblue transition-colors flex-1 md:flex-none justify-center"
|
||||||
>
|
>
|
||||||
<svg className="w-5 h-5 text-lblue" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
<svg className="w-4 h-4 md:w-5 md:h-5 text-lblue" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M3 8l7.89 5.26a2 2 0 002.22 0L21 8M5 19h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v10a2 2 0 002 2z" />
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M3 8l7.89 5.26a2 2 0 002.22 0L21 8M5 19h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v10a2 2 0 002 2z" />
|
||||||
</svg>
|
</svg>
|
||||||
<span className="text-sm font-medium">Envoyer un email</span>
|
<span className="text-xs md:text-sm font-medium">Envoyer un email</span>
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="grid grid-cols-1 lg:grid-cols-2 gap-6">
|
<div className="grid grid-cols-1 lg:grid-cols-2 gap-4 md:gap-6">
|
||||||
{/* Carte Informations personnelles */}
|
{/* Carte Informations personnelles */}
|
||||||
<div className="bg-white rounded-xl border border-gray-200 p-6 shadow-sm">
|
<div className="bg-white rounded-lg md:rounded-xl border border-gray-200 p-4 md:p-6 shadow-sm">
|
||||||
<div className="flex items-center gap-3 mb-6 pb-4 border-b border-gray-200">
|
<div className="flex items-center gap-2 md:gap-3 mb-4 md:mb-6 pb-3 md:pb-4 border-b border-gray-200">
|
||||||
<div className="w-10 h-10 rounded-lg bg-lblue/10 flex items-center justify-center">
|
<div className="w-8 h-8 md:w-10 md:h-10 rounded-lg bg-lblue/10 flex items-center justify-center flex-shrink-0">
|
||||||
<svg className="w-5 h-5 text-lblue" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
<svg className="w-4 h-4 md:w-5 md:h-5 text-lblue" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M16 7a4 4 0 11-8 0 4 4 0 018 0zM12 14a7 7 0 00-7 7h14a7 7 0 00-7-7z" />
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M16 7a4 4 0 11-8 0 4 4 0 018 0zM12 14a7 7 0 00-7 7h14a7 7 0 00-7-7z" />
|
||||||
</svg>
|
</svg>
|
||||||
</div>
|
</div>
|
||||||
<h3 className="text-lg font-semibold text-gray-900">
|
<h3 className="text-base md:text-lg font-semibold text-gray-900">
|
||||||
Informations personnelles
|
Informations personnelles
|
||||||
</h3>
|
</h3>
|
||||||
</div>
|
</div>
|
||||||
<div className="space-y-4">
|
<div className="space-y-3 md:space-y-4">
|
||||||
<div className="flex items-start gap-4">
|
<div className="flex items-start gap-3 md:gap-4">
|
||||||
<div className="w-10 h-10 rounded-lg bg-gray-100 flex items-center justify-center flex-shrink-0">
|
<div className="w-8 h-8 md:w-10 md:h-10 rounded-lg bg-gray-100 flex items-center justify-center flex-shrink-0">
|
||||||
<svg className="w-5 h-5 text-gray-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
<svg className="w-4 h-4 md:w-5 md:h-5 text-gray-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M8 7V3m8 4V3m-9 8h10M5 21h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v12a2 2 0 002 2z" />
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M8 7V3m8 4V3m-9 8h10M5 21h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v12a2 2 0 002 2z" />
|
||||||
</svg>
|
</svg>
|
||||||
</div>
|
</div>
|
||||||
@@ -1107,23 +1222,23 @@ export default function ChauffeursTable() {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="flex items-start gap-4">
|
<div className="flex items-start gap-3 md:gap-4">
|
||||||
<div className="w-10 h-10 rounded-lg bg-gray-100 flex items-center justify-center flex-shrink-0">
|
<div className="w-8 h-8 md:w-10 md:h-10 rounded-lg bg-gray-100 flex items-center justify-center flex-shrink-0">
|
||||||
<svg className="w-5 h-5 text-gray-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
<svg className="w-4 h-4 md:w-5 md:h-5 text-gray-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M3 5a2 2 0 012-2h3.28a1 1 0 01.948.684l1.498 4.493a1 1 0 01-.502 1.21l-2.257 1.13a11.042 11.042 0 005.516 5.516l1.13-2.257a1 1 0 011.21-.502l4.493 1.498a1 1 0 01.684.949V19a2 2 0 01-2 2h-1C9.716 21 3 14.284 3 6V5z" />
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M3 5a2 2 0 012-2h3.28a1 1 0 01.948.684l1.498 4.493a1 1 0 01-.502 1.21l-2.257 1.13a11.042 11.042 0 005.516 5.516l1.13-2.257a1 1 0 011.21-.502l4.493 1.498a1 1 0 01.684.949V19a2 2 0 01-2 2h-1C9.716 21 3 14.284 3 6V5z" />
|
||||||
</svg>
|
</svg>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex-1 min-w-0">
|
<div className="flex-1 min-w-0">
|
||||||
<p className="text-xs font-medium text-gray-500 uppercase tracking-wide mb-1">Téléphone</p>
|
<p className="text-xs font-medium text-gray-500 uppercase tracking-wide mb-1">Téléphone</p>
|
||||||
<a href={`tel:${viewingChauffeur.telephone}`} className="text-sm font-semibold text-lblue hover:text-dblue transition-colors">
|
<a href={`tel:${viewingChauffeur.telephone}`} className="text-sm font-semibold text-lblue hover:text-dblue transition-colors break-all">
|
||||||
{viewingChauffeur.telephone}
|
{viewingChauffeur.telephone}
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="flex items-start gap-4">
|
<div className="flex items-start gap-3 md:gap-4">
|
||||||
<div className="w-10 h-10 rounded-lg bg-gray-100 flex items-center justify-center flex-shrink-0">
|
<div className="w-8 h-8 md:w-10 md:h-10 rounded-lg bg-gray-100 flex items-center justify-center flex-shrink-0">
|
||||||
<svg className="w-5 h-5 text-gray-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
<svg className="w-4 h-4 md:w-5 md:h-5 text-gray-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M3 8l7.89 5.26a2 2 0 002.22 0L21 8M5 19h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v10a2 2 0 002 2z" />
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M3 8l7.89 5.26a2 2 0 002.22 0L21 8M5 19h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v10a2 2 0 002 2z" />
|
||||||
</svg>
|
</svg>
|
||||||
</div>
|
</div>
|
||||||
@@ -1135,49 +1250,49 @@ export default function ChauffeursTable() {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="flex items-start gap-4">
|
<div className="flex items-start gap-3 md:gap-4">
|
||||||
<div className="w-10 h-10 rounded-lg bg-gray-100 flex items-center justify-center flex-shrink-0">
|
<div className="w-8 h-8 md:w-10 md:h-10 rounded-lg bg-gray-100 flex items-center justify-center flex-shrink-0">
|
||||||
<svg className="w-5 h-5 text-gray-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
<svg className="w-4 h-4 md:w-5 md:h-5 text-gray-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M17.657 16.657L13.414 20.9a1.998 1.998 0 01-2.827 0l-4.244-4.243a8 8 0 1111.314 0z" />
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M17.657 16.657L13.414 20.9a1.998 1.998 0 01-2.827 0l-4.244-4.243a8 8 0 1111.314 0z" />
|
||||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M15 11a3 3 0 11-6 0 3 3 0 016 0z" />
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M15 11a3 3 0 11-6 0 3 3 0 016 0z" />
|
||||||
</svg>
|
</svg>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex-1 min-w-0">
|
<div className="flex-1 min-w-0">
|
||||||
<p className="text-xs font-medium text-gray-500 uppercase tracking-wide mb-1">Adresse</p>
|
<p className="text-xs font-medium text-gray-500 uppercase tracking-wide mb-1">Adresse</p>
|
||||||
<p className="text-sm font-semibold text-gray-900">{viewingChauffeur.adresse}</p>
|
<p className="text-sm font-semibold text-gray-900 break-words">{viewingChauffeur.adresse}</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Carte Informations contractuelles */}
|
{/* Carte Informations contractuelles */}
|
||||||
<div className="bg-white rounded-xl border border-gray-200 p-6 shadow-sm">
|
<div className="bg-white rounded-lg md:rounded-xl border border-gray-200 p-4 md:p-6 shadow-sm">
|
||||||
<div className="flex items-center gap-3 mb-6 pb-4 border-b border-gray-200">
|
<div className="flex items-center gap-2 md:gap-3 mb-4 md:mb-6 pb-3 md:pb-4 border-b border-gray-200">
|
||||||
<div className="w-10 h-10 rounded-lg bg-lblue/10 flex items-center justify-center">
|
<div className="w-8 h-8 md:w-10 md:h-10 rounded-lg bg-lblue/10 flex items-center justify-center flex-shrink-0">
|
||||||
<svg className="w-5 h-5 text-lblue" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
<svg className="w-4 h-4 md:w-5 md:h-5 text-lblue" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z" />
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z" />
|
||||||
</svg>
|
</svg>
|
||||||
</div>
|
</div>
|
||||||
<h3 className="text-lg font-semibold text-gray-900">
|
<h3 className="text-base md:text-lg font-semibold text-gray-900">
|
||||||
Informations contractuelles
|
Informations contractuelles
|
||||||
</h3>
|
</h3>
|
||||||
</div>
|
</div>
|
||||||
<div className="space-y-4">
|
<div className="space-y-3 md:space-y-4">
|
||||||
<div className="flex items-start gap-4">
|
<div className="flex items-start gap-3 md:gap-4">
|
||||||
<div className="w-10 h-10 rounded-lg bg-gray-100 flex items-center justify-center flex-shrink-0">
|
<div className="w-8 h-8 md:w-10 md:h-10 rounded-lg bg-gray-100 flex items-center justify-center flex-shrink-0">
|
||||||
<svg className="w-5 h-5 text-gray-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
<svg className="w-4 h-4 md:w-5 md:h-5 text-gray-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M12 8v4l3 3m6-3a9 9 0 11-18 0 9 9 0 0118 0z" />
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M12 8v4l3 3m6-3a9 9 0 11-18 0 9 9 0 0118 0z" />
|
||||||
</svg>
|
</svg>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex-1 min-w-0">
|
<div className="flex-1 min-w-0">
|
||||||
<p className="text-xs font-medium text-gray-500 uppercase tracking-wide mb-1">Contrat d'heure</p>
|
<p className="text-xs font-medium text-gray-500 uppercase tracking-wide mb-1">Contrat d'heure</p>
|
||||||
<p className="text-lg font-bold text-gray-900">{viewingChauffeur.heuresContrat}h</p>
|
<p className="text-base md:text-lg font-bold text-gray-900">{viewingChauffeur.heuresContrat}h</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="flex items-start gap-4">
|
<div className="flex items-start gap-3 md:gap-4">
|
||||||
<div className="w-10 h-10 rounded-lg bg-gray-100 flex items-center justify-center flex-shrink-0">
|
<div className="w-8 h-8 md:w-10 md:h-10 rounded-lg bg-gray-100 flex items-center justify-center flex-shrink-0">
|
||||||
<svg className="w-5 h-5 text-gray-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
<svg className="w-4 h-4 md:w-5 md:h-5 text-gray-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M8 7V3m8 4V3m-9 8h10M5 21h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v12a2 2 0 002 2z" />
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M8 7V3m8 4V3m-9 8h10M5 21h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v12a2 2 0 002 2z" />
|
||||||
</svg>
|
</svg>
|
||||||
</div>
|
</div>
|
||||||
@@ -1188,9 +1303,9 @@ export default function ChauffeursTable() {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{viewingChauffeur.dateFinContrat && (
|
{viewingChauffeur.dateFinContrat && (
|
||||||
<div className="flex items-start gap-4">
|
<div className="flex items-start gap-3 md:gap-4">
|
||||||
<div className="w-10 h-10 rounded-lg bg-gray-100 flex items-center justify-center flex-shrink-0">
|
<div className="w-8 h-8 md:w-10 md:h-10 rounded-lg bg-gray-100 flex items-center justify-center flex-shrink-0">
|
||||||
<svg className="w-5 h-5 text-gray-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
<svg className="w-4 h-4 md:w-5 md:h-5 text-gray-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M8 7V3m8 4V3m-9 8h10M5 21h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v12a2 2 0 002 2z" />
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M8 7V3m8 4V3m-9 8h10M5 21h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v12a2 2 0 002 2z" />
|
||||||
</svg>
|
</svg>
|
||||||
</div>
|
</div>
|
||||||
@@ -1203,15 +1318,15 @@ export default function ChauffeursTable() {
|
|||||||
|
|
||||||
{viewingChauffeur.heuresRestantes !== undefined && (
|
{viewingChauffeur.heuresRestantes !== undefined && (
|
||||||
<div className="pt-2">
|
<div className="pt-2">
|
||||||
<div className="flex items-center justify-between mb-3">
|
<div className="flex items-center justify-between mb-2 md:mb-3">
|
||||||
<p className="text-xs font-medium text-gray-500 uppercase tracking-wide">Heures restantes</p>
|
<p className="text-xs font-medium text-gray-500 uppercase tracking-wide">Heures restantes</p>
|
||||||
<span className="text-lg font-bold text-gray-900">
|
<span className="text-base md:text-lg font-bold text-gray-900">
|
||||||
{viewingChauffeur.heuresRestantes}h <span className="text-sm font-normal text-gray-500">/ {viewingChauffeur.heuresContrat}h</span>
|
{viewingChauffeur.heuresRestantes}h <span className="text-xs md:text-sm font-normal text-gray-500">/ {viewingChauffeur.heuresContrat}h</span>
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div className="relative w-full bg-gray-200 rounded-full h-3 overflow-hidden">
|
<div className="relative w-full bg-gray-200 rounded-full h-2 md:h-3 overflow-hidden">
|
||||||
<div
|
<div
|
||||||
className={`h-3 rounded-full transition-all duration-500 ${
|
className={`h-2 md:h-3 rounded-full transition-all duration-500 ${
|
||||||
(viewingChauffeur.heuresRestantes / viewingChauffeur.heuresContrat) < 0.2
|
(viewingChauffeur.heuresRestantes / viewingChauffeur.heuresContrat) < 0.2
|
||||||
? 'bg-orange-500'
|
? 'bg-orange-500'
|
||||||
: (viewingChauffeur.heuresRestantes / viewingChauffeur.heuresContrat) < 0.5
|
: (viewingChauffeur.heuresRestantes / viewingChauffeur.heuresContrat) < 0.5
|
||||||
@@ -1233,11 +1348,11 @@ export default function ChauffeursTable() {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Footer avec actions */}
|
{/* Footer avec actions */}
|
||||||
<div className="border-t border-gray-200 px-8 py-5 bg-white">
|
<div className="border-t border-gray-200 px-4 md:px-8 py-4 md:py-5 bg-white">
|
||||||
<div className="flex items-center justify-between">
|
<div className="flex flex-col-reverse md:flex-row items-stretch md:items-center justify-between gap-3 md:gap-0">
|
||||||
<button
|
<button
|
||||||
onClick={() => setViewingChauffeur(null)}
|
onClick={() => setViewingChauffeur(null)}
|
||||||
className="px-5 py-2.5 text-sm font-medium text-gray-700 bg-white border border-gray-300 rounded-lg hover:bg-gray-50 transition-colors"
|
className="px-4 md:px-5 py-2.5 text-sm font-medium text-gray-700 bg-white border border-gray-300 rounded-lg hover:bg-gray-50 transition-colors w-full md:w-auto"
|
||||||
>
|
>
|
||||||
Fermer
|
Fermer
|
||||||
</button>
|
</button>
|
||||||
@@ -1246,9 +1361,9 @@ export default function ChauffeursTable() {
|
|||||||
setViewingChauffeur(null);
|
setViewingChauffeur(null);
|
||||||
handleEdit(viewingChauffeur);
|
handleEdit(viewingChauffeur);
|
||||||
}}
|
}}
|
||||||
className="px-6 py-2.5 bg-lblue text-white text-sm font-semibold rounded-lg hover:bg-dblue transition-colors flex items-center gap-2"
|
className="px-4 md:px-6 py-2.5 bg-lblue text-white text-sm font-semibold rounded-lg hover:bg-dblue transition-colors flex items-center justify-center gap-2 w-full md:w-auto"
|
||||||
>
|
>
|
||||||
<svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
<svg className="w-4 h-4 md:w-5 md:h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M11 5H6a2 2 0 00-2 2v11a2 2 0 002 2h11a2 2 0 002-2v-5m-1.414-9.414a2 2 0 112.828 2.828L11.828 15H9v-2.828l8.586-8.586z" />
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M11 5H6a2 2 0 00-2 2v11a2 2 0 002 2h11a2 2 0 002-2v-5m-1.414-9.414a2 2 0 112.828 2.828L11.828 15H9v-2.828l8.586-8.586z" />
|
||||||
</svg>
|
</svg>
|
||||||
Modifier le chauffeur
|
Modifier le chauffeur
|
||||||
|
|||||||
@@ -170,6 +170,7 @@ OptionCard.displayName = 'OptionCard';
|
|||||||
export default function ConfigurationContent() {
|
export default function ConfigurationContent() {
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const { showNotification } = useNotification();
|
const { showNotification } = useNotification();
|
||||||
|
const [isMobile, setIsMobile] = useState(false);
|
||||||
const [activeConfigSection, setActiveConfigSection] = useState<'adherents' | 'comptes' | 'roles' | null>('adherents');
|
const [activeConfigSection, setActiveConfigSection] = useState<'adherents' | 'comptes' | 'roles' | null>('adherents');
|
||||||
const [options, setOptions] = useState<OptionsByType>({
|
const [options, setOptions] = useState<OptionsByType>({
|
||||||
situation: [],
|
situation: [],
|
||||||
@@ -225,6 +226,15 @@ export default function ConfigurationContent() {
|
|||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const checkMobile = () => {
|
||||||
|
setIsMobile(window.innerWidth < 768);
|
||||||
|
};
|
||||||
|
checkMobile();
|
||||||
|
window.addEventListener('resize', checkMobile);
|
||||||
|
return () => window.removeEventListener('resize', checkMobile);
|
||||||
|
}, []);
|
||||||
|
|
||||||
const handleAdd = useCallback(async (type: 'situation' | 'prescripteur' | 'facturation' | 'forfait') => {
|
const handleAdd = useCallback(async (type: 'situation' | 'prescripteur' | 'facturation' | 'forfait') => {
|
||||||
const currentValue = newValue[type]?.trim() || '';
|
const currentValue = newValue[type]?.trim() || '';
|
||||||
if (!currentValue) {
|
if (!currentValue) {
|
||||||
@@ -1097,6 +1107,35 @@ export default function ConfigurationContent() {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Afficher une modale sur mobile
|
||||||
|
if (isMobile) {
|
||||||
|
return (
|
||||||
|
<div className="fixed inset-0 z-50 flex items-center justify-center bg-black/50 p-4">
|
||||||
|
<div className="bg-white rounded-xl shadow-xl max-w-md w-full p-6 md:p-8">
|
||||||
|
<div className="text-center mb-6">
|
||||||
|
<div className="w-16 h-16 md:w-20 md:h-20 rounded-full bg-lblue/10 flex items-center justify-center mx-auto mb-4">
|
||||||
|
<svg className="w-8 h-8 md:w-10 md:h-10 text-lblue" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9.75 17L9 20l-1 1h8l-1-1-.75-3M3 13h18M5 17h14a2 2 0 002-2V5a2 2 0 00-2-2H5a2 2 0 00-2 2v10a2 2 0 002 2z" />
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
<h2 className="text-xl md:text-2xl font-bold text-gray-900 mb-2">
|
||||||
|
Configuration sur ordinateur recommandée
|
||||||
|
</h2>
|
||||||
|
<p className="text-sm md:text-base text-gray-600">
|
||||||
|
Pour une meilleure expérience, veuillez utiliser un ordinateur pour accéder à la configuration de la plateforme.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<button
|
||||||
|
onClick={() => router.push('/dashboard/parametres')}
|
||||||
|
className="w-full px-4 py-3 bg-lblue text-white rounded-lg hover:bg-dblue transition-colors font-medium text-sm md:text-base"
|
||||||
|
>
|
||||||
|
Retour aux paramètres
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="p-6">
|
<div className="p-6">
|
||||||
<div className="mb-6 flex items-center gap-4">
|
<div className="mb-6 flex items-center gap-4">
|
||||||
|
|||||||
@@ -156,54 +156,54 @@ export default function DashboardContent({ userName }: DashboardContentProps) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="p-6 space-y-8">
|
<div className="p-4 sm:p-6 space-y-6 sm:space-y-8">
|
||||||
{/* En-tête */}
|
{/* En-tête */}
|
||||||
<div className="mb-8">
|
<div className="mb-6 sm:mb-8">
|
||||||
<h1 className="text-3xl font-bold text-gray-900 mb-2">
|
<h1 className="text-2xl sm:text-3xl font-bold text-gray-900 mb-2">
|
||||||
Content de vous revoir <span className="text-dyellow">{userName || 'Utilisateur'}</span>
|
Content de vous revoir <span className="text-dyellow">{userName || 'Utilisateur'}</span>
|
||||||
</h1>
|
</h1>
|
||||||
<p className="text-sm text-gray-600">
|
<p className="text-xs sm:text-sm text-gray-600">
|
||||||
Bienvenue sur votre tableau de bord.
|
Bienvenue sur votre tableau de bord.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Statistiques */}
|
{/* Statistiques */}
|
||||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-5">
|
<div className="grid grid-cols-2 lg:grid-cols-4 gap-4 sm:gap-5">
|
||||||
{/* Participations du mois */}
|
{/* Participations du mois */}
|
||||||
<div className="bg-white rounded-xl shadow-sm p-6 border border-gray-100 hover:shadow-lg hover:border-dyellow/30 transition-all duration-300 group">
|
<div className="bg-white rounded-xl shadow-sm p-4 sm:p-6 border border-gray-100 hover:shadow-lg hover:border-dyellow/30 transition-all duration-300 group">
|
||||||
<div className="flex items-start justify-between mb-4">
|
<div className="flex items-start justify-between mb-3 sm:mb-4">
|
||||||
<div className="w-12 h-12 rounded-xl bg-gradient-to-br from-dyellow/20 to-dyellow/10 flex items-center justify-center flex-shrink-0 group-hover:scale-110 transition-transform duration-300">
|
<div className="w-10 h-10 sm:w-12 sm:h-12 rounded-xl bg-gradient-to-br from-dyellow/20 to-dyellow/10 flex items-center justify-center flex-shrink-0 group-hover:scale-110 transition-transform duration-300">
|
||||||
<svg className="w-6 h-6 text-dyellow" fill="currentColor" viewBox="0 0 24 24">
|
<svg className="w-5 h-5 sm:w-6 sm:h-6 text-dyellow" fill="currentColor" viewBox="0 0 24 24">
|
||||||
<path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm1.41 16.09V20h-2.67v-1.93c-1.71-.36-3.16-1.46-3.27-3.4h1.96c.1 1.05.82 1.87 2.65 1.87 1.96 0 2.4-.98 2.4-1.59 0-.83-.44-1.61-2.67-2.14-2.48-.6-4.18-1.62-4.18-3.67 0-1.72 1.39-2.84 3.11-3.21V4h2.67v1.95c1.86.45 2.79 1.86 2.85 3.39H14.3c-.05-1.11-.64-1.87-2.22-1.87-1.5 0-2.4.68-2.4 1.64 0 .84.65 1.39 2.67 1.91s4.18 1.39 4.18 3.91c-.01 1.83-1.38 2.83-3.12 3.16z"/>
|
<path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm1.41 16.09V20h-2.67v-1.93c-1.71-.36-3.16-1.46-3.27-3.4h1.96c.1 1.05.82 1.87 2.65 1.87 1.96 0 2.4-.98 2.4-1.59 0-.83-.44-1.61-2.67-2.14-2.48-.6-4.18-1.62-4.18-3.67 0-1.72 1.39-2.84 3.11-3.21V4h2.67v1.95c1.86.45 2.79 1.86 2.85 3.39H14.3c-.05-1.11-.64-1.87-2.22-1.87-1.5 0-2.4.68-2.4 1.64 0 .84.65 1.39 2.67 1.91s4.18 1.39 4.18 3.91c-.01 1.83-1.38 2.83-3.12 3.16z"/>
|
||||||
</svg>
|
</svg>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<p className="text-xs text-gray-600 mb-2 font-medium uppercase tracking-wide">Participations du mois</p>
|
<p className="text-[10px] sm:text-xs text-gray-600 mb-1.5 sm:mb-2 font-medium uppercase tracking-wide">Participations du mois</p>
|
||||||
<p className="text-3xl font-bold text-gray-900 mb-2">
|
<p className="text-2xl sm:text-3xl font-bold text-gray-900 mb-1.5 sm:mb-2">
|
||||||
{stats ? `${stats.participationsMois.montant.toFixed(2).replace('.', ',')}€` : '0,00€'}
|
{stats ? `${stats.participationsMois.montant.toFixed(2).replace('.', ',')}€` : '0,00€'}
|
||||||
</p>
|
</p>
|
||||||
<p className="text-xs text-gray-500 font-medium">
|
<p className="text-[10px] sm:text-xs text-gray-500 font-medium">
|
||||||
{stats ? `${stats.participationsMois.nombreFactures} ${stats.participationsMois.nombreFactures > 1 ? 'Factures' : 'Facture'}` : '0 Facture'}
|
{stats ? `${stats.participationsMois.nombreFactures} ${stats.participationsMois.nombreFactures > 1 ? 'Factures' : 'Facture'}` : '0 Facture'}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Trajets Aujourd'hui */}
|
{/* Trajets Aujourd'hui */}
|
||||||
<div className="bg-white rounded-xl shadow-sm p-6 border border-gray-100 hover:shadow-lg hover:border-dyellow/30 transition-all duration-300 group">
|
<div className="bg-white rounded-xl shadow-sm p-4 sm:p-6 border border-gray-100 hover:shadow-lg hover:border-dyellow/30 transition-all duration-300 group">
|
||||||
<div className="flex items-start justify-between mb-4">
|
<div className="flex items-start justify-between mb-3 sm:mb-4">
|
||||||
<div className="w-12 h-12 rounded-xl bg-gradient-to-br from-dyellow/20 to-dyellow/10 flex items-center justify-center flex-shrink-0 group-hover:scale-110 transition-transform duration-300">
|
<div className="w-10 h-10 sm:w-12 sm:h-12 rounded-xl bg-gradient-to-br from-dyellow/20 to-dyellow/10 flex items-center justify-center flex-shrink-0 group-hover:scale-110 transition-transform duration-300">
|
||||||
<svg className="w-6 h-6 text-dyellow" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
<svg className="w-5 h-5 sm:w-6 sm:h-6 text-dyellow" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M13 7h8m0 0v8m0-8l-8 8-4-4-6 6" />
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M13 7h8m0 0v8m0-8l-8 8-4-4-6 6" />
|
||||||
</svg>
|
</svg>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<p className="text-xs text-gray-600 mb-2 font-medium uppercase tracking-wide">Trajets Aujourd'hui</p>
|
<p className="text-[10px] sm:text-xs text-gray-600 mb-1.5 sm:mb-2 font-medium uppercase tracking-wide">Trajets Aujourd'hui</p>
|
||||||
<p className="text-3xl font-bold text-gray-900 mb-2">
|
<p className="text-2xl sm:text-3xl font-bold text-gray-900 mb-1.5 sm:mb-2">
|
||||||
{stats ? stats.trajetsAujourdhui.nombre : 0}
|
{stats ? stats.trajetsAujourdhui.nombre : 0}
|
||||||
</p>
|
</p>
|
||||||
<p className="text-xs text-gray-500 font-medium">
|
<p className="text-[10px] sm:text-xs text-gray-500 font-medium">
|
||||||
{stats && stats.trajetsAujourdhui.difference !== 0
|
{stats && stats.trajetsAujourdhui.difference !== 0
|
||||||
? `${stats.trajetsAujourdhui.difference > 0 ? '+' : ''}${stats.trajetsAujourdhui.difference} vs hier`
|
? `${stats.trajetsAujourdhui.difference > 0 ? '+' : ''}${stats.trajetsAujourdhui.difference} vs hier`
|
||||||
: 'Aucun changement'}
|
: 'Aucun changement'}
|
||||||
@@ -212,20 +212,20 @@ export default function DashboardContent({ userName }: DashboardContentProps) {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Trajets réalisés ce mois */}
|
{/* Trajets réalisés ce mois */}
|
||||||
<div className="bg-white rounded-xl shadow-sm p-6 border border-gray-100 hover:shadow-lg hover:border-dyellow/30 transition-all duration-300 group">
|
<div className="bg-white rounded-xl shadow-sm p-4 sm:p-6 border border-gray-100 hover:shadow-lg hover:border-dyellow/30 transition-all duration-300 group">
|
||||||
<div className="flex items-start justify-between mb-4">
|
<div className="flex items-start justify-between mb-3 sm:mb-4">
|
||||||
<div className="w-12 h-12 rounded-xl bg-gradient-to-br from-dyellow/20 to-dyellow/10 flex items-center justify-center flex-shrink-0 group-hover:scale-110 transition-transform duration-300">
|
<div className="w-10 h-10 sm:w-12 sm:h-12 rounded-xl bg-gradient-to-br from-dyellow/20 to-dyellow/10 flex items-center justify-center flex-shrink-0 group-hover:scale-110 transition-transform duration-300">
|
||||||
<svg className="w-6 h-6 text-dyellow" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
<svg className="w-5 h-5 sm:w-6 sm:h-6 text-dyellow" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z" />
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z" />
|
||||||
</svg>
|
</svg>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<p className="text-xs text-gray-600 mb-2 font-medium uppercase tracking-wide">Trajets réalisés ce mois</p>
|
<p className="text-[10px] sm:text-xs text-gray-600 mb-1.5 sm:mb-2 font-medium uppercase tracking-wide">Trajets réalisés ce mois</p>
|
||||||
<p className="text-3xl font-bold text-gray-900 mb-2">
|
<p className="text-2xl sm:text-3xl font-bold text-gray-900 mb-1.5 sm:mb-2">
|
||||||
{stats ? stats.trajetsRealisesMois.nombre : 0}
|
{stats ? stats.trajetsRealisesMois.nombre : 0}
|
||||||
</p>
|
</p>
|
||||||
<p className="text-xs text-gray-500 font-medium">
|
<p className="text-[10px] sm:text-xs text-gray-500 font-medium">
|
||||||
{stats && stats.trajetsRealisesMois.pourcentageEvolution !== 0
|
{stats && stats.trajetsRealisesMois.pourcentageEvolution !== 0
|
||||||
? `${stats.trajetsRealisesMois.pourcentageEvolution > 0 ? '+' : ''}${stats.trajetsRealisesMois.pourcentageEvolution}% vs mois dernier`
|
? `${stats.trajetsRealisesMois.pourcentageEvolution > 0 ? '+' : ''}${stats.trajetsRealisesMois.pourcentageEvolution}% vs mois dernier`
|
||||||
: 'Aucun changement'}
|
: 'Aucun changement'}
|
||||||
@@ -234,20 +234,20 @@ export default function DashboardContent({ userName }: DashboardContentProps) {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Chauffeurs actifs */}
|
{/* Chauffeurs actifs */}
|
||||||
<div className="bg-white rounded-xl shadow-sm p-6 border border-gray-100 hover:shadow-lg hover:border-dyellow/30 transition-all duration-300 group">
|
<div className="bg-white rounded-xl shadow-sm p-4 sm:p-6 border border-gray-100 hover:shadow-lg hover:border-dyellow/30 transition-all duration-300 group">
|
||||||
<div className="flex items-start justify-between mb-4">
|
<div className="flex items-start justify-between mb-3 sm:mb-4">
|
||||||
<div className="w-12 h-12 rounded-xl bg-gradient-to-br from-dyellow/20 to-dyellow/10 flex items-center justify-center flex-shrink-0 group-hover:scale-110 transition-transform duration-300">
|
<div className="w-10 h-10 sm:w-12 sm:h-12 rounded-xl bg-gradient-to-br from-dyellow/20 to-dyellow/10 flex items-center justify-center flex-shrink-0 group-hover:scale-110 transition-transform duration-300">
|
||||||
<svg className="w-6 h-6 text-dyellow" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
<svg className="w-5 h-5 sm:w-6 sm:h-6 text-dyellow" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M17 20h5v-2a3 3 0 00-5.356-1.857M17 20H7m10 0v-2c0-.656-.126-1.283-.356-1.857M7 20H2v-2a3 3 0 015.356-1.857M7 20v-2c0-.656.126-1.283.356-1.857m0 0a5.002 5.002 0 019.288 0M15 7a3 3 0 11-6 0 3 3 0 016 0zm6 3a2 2 0 11-4 0 2 2 0 014 0zM7 10a2 2 0 11-4 0 2 2 0 014 0z" />
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M17 20h5v-2a3 3 0 00-5.356-1.857M17 20H7m10 0v-2c0-.656-.126-1.283-.356-1.857M7 20H2v-2a3 3 0 015.356-1.857M7 20v-2c0-.656.126-1.283.356-1.857m0 0a5.002 5.002 0 019.288 0M15 7a3 3 0 11-6 0 3 3 0 016 0zm6 3a2 2 0 11-4 0 2 2 0 014 0zM7 10a2 2 0 11-4 0 2 2 0 014 0z" />
|
||||||
</svg>
|
</svg>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<p className="text-xs text-gray-600 mb-2 font-medium uppercase tracking-wide">Chauffeurs actifs</p>
|
<p className="text-[10px] sm:text-xs text-gray-600 mb-1.5 sm:mb-2 font-medium uppercase tracking-wide">Chauffeurs actifs</p>
|
||||||
<p className="text-3xl font-bold text-gray-900 mb-2">
|
<p className="text-2xl sm:text-3xl font-bold text-gray-900 mb-1.5 sm:mb-2">
|
||||||
{stats ? stats.chauffeursActifs.nombre : 0}
|
{stats ? stats.chauffeursActifs.nombre : 0}
|
||||||
</p>
|
</p>
|
||||||
<p className="text-xs text-gray-500 font-medium">
|
<p className="text-[10px] sm:text-xs text-gray-500 font-medium">
|
||||||
{stats ? `Sur ${stats.chauffeursActifs.total} total` : 'Sur 0 total'}
|
{stats ? `Sur ${stats.chauffeursActifs.total} total` : 'Sur 0 total'}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
@@ -255,126 +255,126 @@ export default function DashboardContent({ userName }: DashboardContentProps) {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Actions Rapides et Trajets Récents côte à côte */}
|
{/* Actions Rapides et Trajets Récents côte à côte */}
|
||||||
<div className="grid grid-cols-1 lg:grid-cols-2 gap-6">
|
<div className="grid grid-cols-1 lg:grid-cols-2 gap-5 sm:gap-6">
|
||||||
{/* Actions Rapides */}
|
{/* Actions Rapides */}
|
||||||
<div>
|
<div>
|
||||||
<div className="flex items-center justify-between mb-4">
|
<div className="flex items-center justify-between mb-3 sm:mb-4">
|
||||||
<h2 className="text-xl font-bold text-gray-900">Actions Rapides</h2>
|
<h2 className="text-lg sm:text-xl font-bold text-gray-900">Actions Rapides</h2>
|
||||||
</div>
|
</div>
|
||||||
<div className="grid grid-cols-2 gap-4">
|
<div className="grid grid-cols-2 gap-3 sm:gap-4">
|
||||||
<button
|
<button
|
||||||
onClick={() => setShowTrajetForm(true)}
|
onClick={() => setShowTrajetForm(true)}
|
||||||
className="bg-white rounded-xl shadow-sm p-6 border border-gray-100 hover:border-lblue hover:shadow-lg transition-all duration-300 text-left group relative overflow-hidden"
|
className="bg-white rounded-xl shadow-sm p-4 sm:p-6 border border-gray-100 hover:border-lblue hover:shadow-lg transition-all duration-300 text-left group relative overflow-hidden"
|
||||||
>
|
>
|
||||||
<div className="absolute inset-0 bg-gradient-to-br from-lblue/5 to-transparent opacity-0 group-hover:opacity-100 transition-opacity duration-300"></div>
|
<div className="absolute inset-0 bg-gradient-to-br from-lblue/5 to-transparent opacity-0 group-hover:opacity-100 transition-opacity duration-300"></div>
|
||||||
<div className="relative">
|
<div className="relative">
|
||||||
<div className="w-12 h-12 rounded-xl bg-gradient-to-br from-lblue/15 to-lblue/5 flex items-center justify-center mb-4 group-hover:scale-110 group-hover:from-lblue/25 group-hover:to-lblue/15 transition-all duration-300">
|
<div className="w-10 h-10 sm:w-12 sm:h-12 rounded-xl bg-gradient-to-br from-lblue/15 to-lblue/5 flex items-center justify-center mb-3 sm:mb-4 group-hover:scale-110 group-hover:from-lblue/25 group-hover:to-lblue/15 transition-all duration-300">
|
||||||
<svg className="w-6 h-6 text-lblue" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
<svg className="w-5 h-5 sm:w-6 sm:h-6 text-lblue" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9 20l-5.447-2.724A1 1 0 013 16.382V5.618a1 1 0 011.447-.894L9 7m0 13l6-3m-6 3V7m6 10l4.553 2.276A1 1 0 0021 18.382V7.618a1 1 0 00-.553-.894L15 4m0 13V4m0 0L9 7" />
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9 20l-5.447-2.724A1 1 0 013 16.382V5.618a1 1 0 011.447-.894L9 7m0 13l6-3m-6 3V7m6 10l4.553 2.276A1 1 0 0021 18.382V7.618a1 1 0 00-.553-.894L15 4m0 13V4m0 0L9 7" />
|
||||||
</svg>
|
</svg>
|
||||||
</div>
|
</div>
|
||||||
<h3 className="text-sm font-bold text-gray-900 group-hover:text-lblue transition-colors">Nouveau trajet</h3>
|
<h3 className="text-xs sm:text-sm font-bold text-gray-900 group-hover:text-lblue transition-colors">Nouveau trajet</h3>
|
||||||
</div>
|
</div>
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<button
|
<button
|
||||||
onClick={() => setShowAdherentForm(true)}
|
onClick={() => setShowAdherentForm(true)}
|
||||||
className="bg-white rounded-xl shadow-sm p-6 border border-gray-100 hover:border-lblue hover:shadow-lg transition-all duration-300 text-left group relative overflow-hidden"
|
className="bg-white rounded-xl shadow-sm p-4 sm:p-6 border border-gray-100 hover:border-lblue hover:shadow-lg transition-all duration-300 text-left group relative overflow-hidden"
|
||||||
>
|
>
|
||||||
<div className="absolute inset-0 bg-gradient-to-br from-lblue/5 to-transparent opacity-0 group-hover:opacity-100 transition-opacity duration-300"></div>
|
<div className="absolute inset-0 bg-gradient-to-br from-lblue/5 to-transparent opacity-0 group-hover:opacity-100 transition-opacity duration-300"></div>
|
||||||
<div className="relative">
|
<div className="relative">
|
||||||
<div className="w-12 h-12 rounded-xl bg-gradient-to-br from-lblue/15 to-lblue/5 flex items-center justify-center mb-4 group-hover:scale-110 group-hover:from-lblue/25 group-hover:to-lblue/15 transition-all duration-300">
|
<div className="w-10 h-10 sm:w-12 sm:h-12 rounded-xl bg-gradient-to-br from-lblue/15 to-lblue/5 flex items-center justify-center mb-3 sm:mb-4 group-hover:scale-110 group-hover:from-lblue/25 group-hover:to-lblue/15 transition-all duration-300">
|
||||||
<svg className="w-6 h-6 text-lblue" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
<svg className="w-5 h-5 sm:w-6 sm:h-6 text-lblue" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M12 4.354a4 4 0 110 5.292M15 21H3v-1a6 6 0 0112 0v1zm0 0h6v-1a6 6 0 00-9-5.197M13 7a4 4 0 11-8 0 4 4 0 018 0z" />
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M12 4.354a4 4 0 110 5.292M15 21H3v-1a6 6 0 0112 0v1zm0 0h6v-1a6 6 0 00-9-5.197M13 7a4 4 0 11-8 0 4 4 0 018 0z" />
|
||||||
</svg>
|
</svg>
|
||||||
</div>
|
</div>
|
||||||
<h3 className="text-sm font-bold text-gray-900 group-hover:text-lblue transition-colors">Nouvel adhérent</h3>
|
<h3 className="text-xs sm:text-sm font-bold text-gray-900 group-hover:text-lblue transition-colors">Nouvel adhérent</h3>
|
||||||
</div>
|
</div>
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<button
|
<button
|
||||||
onClick={() => router.push('/dashboard/factures')}
|
onClick={() => router.push('/dashboard/factures')}
|
||||||
className="bg-white rounded-xl shadow-sm p-6 border border-gray-100 hover:border-lblue hover:shadow-lg transition-all duration-300 text-left group relative overflow-hidden"
|
className="bg-white rounded-xl shadow-sm p-4 sm:p-6 border border-gray-100 hover:border-lblue hover:shadow-lg transition-all duration-300 text-left group relative overflow-hidden"
|
||||||
>
|
>
|
||||||
<div className="absolute inset-0 bg-gradient-to-br from-lblue/5 to-transparent opacity-0 group-hover:opacity-100 transition-opacity duration-300"></div>
|
<div className="absolute inset-0 bg-gradient-to-br from-lblue/5 to-transparent opacity-0 group-hover:opacity-100 transition-opacity duration-300"></div>
|
||||||
<div className="relative">
|
<div className="relative">
|
||||||
<div className="w-12 h-12 rounded-xl bg-gradient-to-br from-lblue/15 to-lblue/5 flex items-center justify-center mb-4 group-hover:scale-110 group-hover:from-lblue/25 group-hover:to-lblue/15 transition-all duration-300">
|
<div className="w-10 h-10 sm:w-12 sm:h-12 rounded-xl bg-gradient-to-br from-lblue/15 to-lblue/5 flex items-center justify-center mb-3 sm:mb-4 group-hover:scale-110 group-hover:from-lblue/25 group-hover:to-lblue/15 transition-all duration-300">
|
||||||
<svg className="w-6 h-6 text-lblue" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
<svg className="w-5 h-5 sm:w-6 sm:h-6 text-lblue" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z" />
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z" />
|
||||||
</svg>
|
</svg>
|
||||||
</div>
|
</div>
|
||||||
<h3 className="text-sm font-bold text-gray-900 group-hover:text-lblue transition-colors">Nouvelle facture</h3>
|
<h3 className="text-xs sm:text-sm font-bold text-gray-900 group-hover:text-lblue transition-colors">Nouvelle facture</h3>
|
||||||
</div>
|
</div>
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<div className="bg-white rounded-xl shadow-sm p-6 border border-gray-100 opacity-60 cursor-not-allowed">
|
<div className="bg-white rounded-xl shadow-sm p-4 sm:p-6 border border-gray-100 opacity-60 cursor-not-allowed">
|
||||||
<div className="w-12 h-12 rounded-xl bg-gray-100 flex items-center justify-center mb-4">
|
<div className="w-10 h-10 sm:w-12 sm:h-12 rounded-xl bg-gray-100 flex items-center justify-center mb-3 sm:mb-4">
|
||||||
<svg className="w-6 h-6 text-gray-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
<svg className="w-5 h-5 sm:w-6 sm:h-6 text-gray-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M12 8v4l3 3m6-3a9 9 0 11-18 0 9 9 0 0118 0z" />
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M12 8v4l3 3m6-3a9 9 0 11-18 0 9 9 0 0118 0z" />
|
||||||
</svg>
|
</svg>
|
||||||
</div>
|
</div>
|
||||||
<h3 className="text-sm font-bold text-gray-400">Bientôt ?</h3>
|
<h3 className="text-xs sm:text-sm font-bold text-gray-400">Bientôt ?</h3>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Trajets Récents */}
|
{/* Trajets Récents */}
|
||||||
<div>
|
<div>
|
||||||
<div className="flex items-center justify-between mb-4">
|
<div className="flex items-center justify-between mb-3 sm:mb-4">
|
||||||
<h2 className="text-xl font-bold text-gray-900">Trajets Récents</h2>
|
<h2 className="text-lg sm:text-xl font-bold text-gray-900">Trajets Récents</h2>
|
||||||
<button
|
<button
|
||||||
onClick={fetchTrajetsRecents}
|
onClick={fetchTrajetsRecents}
|
||||||
className="text-sm text-lblue hover:text-dblue font-medium flex items-center gap-2"
|
className="text-xs sm:text-sm text-lblue hover:text-dblue font-medium flex items-center gap-1 sm:gap-2"
|
||||||
>
|
>
|
||||||
<svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
<svg className="w-3.5 h-3.5 sm:w-4 sm:h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15" />
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15" />
|
||||||
</svg>
|
</svg>
|
||||||
Actualiser
|
<span className="hidden sm:inline">Actualiser</span>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<div className="bg-white rounded-xl shadow-sm border border-gray-100 overflow-hidden">
|
<div className="bg-white rounded-xl shadow-sm border border-gray-100 overflow-hidden">
|
||||||
{loading ? (
|
{loading ? (
|
||||||
<div className="text-center py-8 text-gray-500">Chargement...</div>
|
<div className="text-center py-6 sm:py-8 text-sm text-gray-500">Chargement...</div>
|
||||||
) : trajetsRecents.length === 0 ? (
|
) : trajetsRecents.length === 0 ? (
|
||||||
<div className="text-center py-8 text-gray-500">
|
<div className="text-center py-6 sm:py-8 text-sm text-gray-500">
|
||||||
Aucun trajet créé récemment
|
Aucun trajet créé récemment
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<div className="p-6 space-y-3">
|
<div className="p-4 sm:p-6 space-y-3">
|
||||||
{trajetsRecents.map((trajet) => (
|
{trajetsRecents.map((trajet) => (
|
||||||
<div
|
<div
|
||||||
key={trajet.id}
|
key={trajet.id}
|
||||||
onClick={() => setSelectedTrajet(trajet)}
|
onClick={() => setSelectedTrajet(trajet)}
|
||||||
className="p-4 bg-gray-50 rounded-lg border border-gray-200 hover:border-gray-300 hover:shadow-sm transition-all cursor-pointer"
|
className="p-3 sm:p-4 bg-gray-50 rounded-lg border border-gray-200 hover:border-gray-300 hover:shadow-sm transition-all cursor-pointer"
|
||||||
>
|
>
|
||||||
<div className="flex items-start gap-4">
|
<div className="flex items-start gap-3 sm:gap-4">
|
||||||
{/* Avatar adhérent */}
|
{/* Avatar adhérent */}
|
||||||
<div className="w-12 h-12 rounded-full bg-lgreen flex items-center justify-center text-white text-sm font-semibold flex-shrink-0">
|
<div className="w-10 h-10 sm:w-12 sm:h-12 rounded-full bg-lgreen flex items-center justify-center text-white text-xs sm:text-sm font-semibold flex-shrink-0">
|
||||||
{getInitials(trajet.adherent.nom, trajet.adherent.prenom)}
|
{getInitials(trajet.adherent.nom, trajet.adherent.prenom)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Informations principales */}
|
{/* Informations principales */}
|
||||||
<div className="flex-1 min-w-0">
|
<div className="flex-1 min-w-0">
|
||||||
<div className="flex items-start justify-between gap-4 mb-2">
|
<div className="flex items-start justify-between gap-2 sm:gap-4 mb-2">
|
||||||
<div>
|
<div className="flex-1 min-w-0">
|
||||||
<h3 className="text-sm font-semibold text-gray-900">
|
<h3 className="text-xs sm:text-sm font-semibold text-gray-900 truncate">
|
||||||
{trajet.adherent.prenom} {trajet.adherent.nom}
|
{trajet.adherent.prenom} {trajet.adherent.nom}
|
||||||
</h3>
|
</h3>
|
||||||
<div className="flex items-center gap-3 mt-1">
|
<div className="flex flex-wrap items-center gap-2 sm:gap-3 mt-1">
|
||||||
<span className="text-xs text-gray-500 flex items-center gap-1">
|
<span className="text-[10px] sm:text-xs text-gray-500 flex items-center gap-1">
|
||||||
<svg className="w-3 h-3" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
<svg className="w-2.5 h-2.5 sm:w-3 sm:h-3" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M8 7V3m8 4V3m-9 8h10M5 21h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v12a2 2 0 002 2z" />
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M8 7V3m8 4V3m-9 8h10M5 21h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v12a2 2 0 002 2z" />
|
||||||
</svg>
|
</svg>
|
||||||
{formatDate(trajet.date)}
|
{formatDate(trajet.date)}
|
||||||
</span>
|
</span>
|
||||||
<span className="text-xs text-gray-500 flex items-center gap-1">
|
<span className="text-[10px] sm:text-xs text-gray-500 flex items-center gap-1">
|
||||||
<svg className="w-3 h-3" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
<svg className="w-2.5 h-2.5 sm:w-3 sm:h-3" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M12 8v4l3 3m6-3a9 9 0 11-18 0 9 9 0 0118 0z" />
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M12 8v4l3 3m6-3a9 9 0 11-18 0 9 9 0 0118 0z" />
|
||||||
</svg>
|
</svg>
|
||||||
{formatTime(trajet.date)}
|
{formatTime(trajet.date)}
|
||||||
</span>
|
</span>
|
||||||
<span
|
<span
|
||||||
className={`px-2 py-0.5 text-xs font-medium rounded ${
|
className={`px-1.5 sm:px-2 py-0.5 text-[10px] sm:text-xs font-medium rounded ${
|
||||||
trajet.statut === 'Terminé'
|
trajet.statut === 'Terminé'
|
||||||
? 'bg-green-100 text-green-700'
|
? 'bg-green-100 text-green-700'
|
||||||
: trajet.statut === 'En cours'
|
: trajet.statut === 'En cours'
|
||||||
@@ -394,36 +394,36 @@ export default function DashboardContent({ userName }: DashboardContentProps) {
|
|||||||
|
|
||||||
{/* Adresses */}
|
{/* Adresses */}
|
||||||
<div className="space-y-1 mb-2">
|
<div className="space-y-1 mb-2">
|
||||||
<div className="text-sm text-gray-600">
|
<div className="text-xs sm:text-sm text-gray-600">
|
||||||
<span className="font-medium">Départ:</span>{' '}
|
<span className="font-medium">Départ:</span>{' '}
|
||||||
<span className="text-gray-900">{trajet.adresseDepart}</span>
|
<span className="text-gray-900 break-words">{trajet.adresseDepart}</span>
|
||||||
</div>
|
</div>
|
||||||
<div className="text-sm text-gray-600">
|
<div className="text-xs sm:text-sm text-gray-600">
|
||||||
<span className="font-medium">Arrivée:</span>{' '}
|
<span className="font-medium">Arrivée:</span>{' '}
|
||||||
<span className="text-gray-900">{trajet.adresseArrivee}</span>
|
<span className="text-gray-900 break-words">{trajet.adresseArrivee}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Chauffeur */}
|
{/* Chauffeur */}
|
||||||
<div className="flex items-center gap-4 mt-3">
|
<div className="flex items-center gap-2 sm:gap-4 mt-2 sm:mt-3">
|
||||||
{trajet.chauffeur ? (
|
{trajet.chauffeur ? (
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
<div className="w-8 h-8 rounded-full bg-lblue flex items-center justify-center text-white text-xs font-semibold">
|
<div className="w-7 h-7 sm:w-8 sm:h-8 rounded-full bg-lblue flex items-center justify-center text-white text-[10px] sm:text-xs font-semibold">
|
||||||
{getInitials(trajet.chauffeur.nom, trajet.chauffeur.prenom)}
|
{getInitials(trajet.chauffeur.nom, trajet.chauffeur.prenom)}
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<div className="text-xs text-gray-500">Chauffeur</div>
|
<div className="text-[10px] sm:text-xs text-gray-500">Chauffeur</div>
|
||||||
<div className="text-sm font-medium text-gray-900">
|
<div className="text-xs sm:text-sm font-medium text-gray-900">
|
||||||
{trajet.chauffeur.prenom} {trajet.chauffeur.nom}
|
{trajet.chauffeur.prenom} {trajet.chauffeur.nom}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-1.5 sm:gap-2">
|
||||||
<svg className="w-5 h-5 text-orange-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
<svg className="w-4 h-4 sm:w-5 sm:h-5 text-orange-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z" />
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z" />
|
||||||
</svg>
|
</svg>
|
||||||
<span className="text-sm text-orange-600 font-medium">
|
<span className="text-xs sm:text-sm text-orange-600 font-medium">
|
||||||
Aucun chauffeur assigné
|
Aucun chauffeur assigné
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
@@ -433,7 +433,7 @@ export default function DashboardContent({ userName }: DashboardContentProps) {
|
|||||||
{/* Commentaire */}
|
{/* Commentaire */}
|
||||||
{trajet.commentaire && (
|
{trajet.commentaire && (
|
||||||
<div className="mt-2 pt-2 border-t border-gray-200">
|
<div className="mt-2 pt-2 border-t border-gray-200">
|
||||||
<p className="text-xs text-gray-500 italic">{trajet.commentaire}</p>
|
<p className="text-[10px] sm:text-xs text-gray-500 italic break-words">{trajet.commentaire}</p>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -33,6 +33,7 @@ export default function DashboardLayout({ user, children }: DashboardLayoutProps
|
|||||||
const [loading, setLoading] = useState(false);
|
const [loading, setLoading] = useState(false);
|
||||||
const [showNotifications, setShowNotifications] = useState(false);
|
const [showNotifications, setShowNotifications] = useState(false);
|
||||||
const [showProfileMenu, setShowProfileMenu] = useState(false);
|
const [showProfileMenu, setShowProfileMenu] = useState(false);
|
||||||
|
const [sidebarOpen, setSidebarOpen] = useState(false);
|
||||||
|
|
||||||
// Récupérer les conversations pour compter les messages non lus
|
// Récupérer les conversations pour compter les messages non lus
|
||||||
const { data: conversations } = useSWR<Array<{ unreadCount: number }>>(
|
const { data: conversations } = useSWR<Array<{ unreadCount: number }>>(
|
||||||
@@ -190,10 +191,20 @@ export default function DashboardLayout({ user, children }: DashboardLayoutProps
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="min-h-screen bg-cwhite flex">
|
<div className="min-h-screen bg-cwhite flex">
|
||||||
|
{/* Mobile Sidebar Overlay */}
|
||||||
|
{sidebarOpen && (
|
||||||
|
<div
|
||||||
|
className="fixed inset-0 bg-black/50 z-40 lg:hidden"
|
||||||
|
onClick={() => setSidebarOpen(false)}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
|
||||||
{/* Sidebar */}
|
{/* Sidebar */}
|
||||||
<aside className="w-64 bg-white flex flex-col h-screen sticky top-0 border-r border-gray-200">
|
<aside className={`fixed lg:static inset-y-0 left-0 z-50 w-64 bg-white flex flex-col h-screen border-r border-gray-200 transform transition-transform duration-300 ease-in-out ${
|
||||||
|
sidebarOpen ? 'translate-x-0' : '-translate-x-full lg:translate-x-0'
|
||||||
|
}`}>
|
||||||
{/* Logo Section */}
|
{/* Logo Section */}
|
||||||
<div className="py-5 m-auto">
|
<div className="py-5 px-4 flex items-center justify-between lg:justify-center">
|
||||||
<div className="flex items-center gap-3">
|
<div className="flex items-center gap-3">
|
||||||
<Image
|
<Image
|
||||||
src="/logo.svg"
|
src="/logo.svg"
|
||||||
@@ -202,9 +213,19 @@ export default function DashboardLayout({ user, children }: DashboardLayoutProps
|
|||||||
height={32}
|
height={32}
|
||||||
/>
|
/>
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
<span className="font-semibold text-gray-900">Association MAD</span>
|
<span className="font-semibold text-gray-900 hidden sm:inline">Association MAD</span>
|
||||||
|
<span className="font-semibold text-gray-900 sm:hidden">MAD</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
{/* Close button mobile */}
|
||||||
|
<button
|
||||||
|
onClick={() => setSidebarOpen(false)}
|
||||||
|
className="lg:hidden p-2 rounded-lg hover:bg-gray-100 transition-colors"
|
||||||
|
>
|
||||||
|
<svg className="w-6 h-6 text-gray-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M6 18L18 6M6 6l12 12" />
|
||||||
|
</svg>
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Navigation */}
|
{/* Navigation */}
|
||||||
@@ -228,6 +249,7 @@ export default function DashboardLayout({ user, children }: DashboardLayoutProps
|
|||||||
<li key={item.href}>
|
<li key={item.href}>
|
||||||
<Link
|
<Link
|
||||||
href={item.href}
|
href={item.href}
|
||||||
|
onClick={() => setSidebarOpen(false)}
|
||||||
className={`flex items-center gap-3 px-4 py-3 rounded-lg transition-colors relative ${
|
className={`flex items-center gap-3 px-4 py-3 rounded-lg transition-colors relative ${
|
||||||
isActive
|
isActive
|
||||||
? 'bg-lblue text-white'
|
? 'bg-lblue text-white'
|
||||||
@@ -261,22 +283,32 @@ export default function DashboardLayout({ user, children }: DashboardLayoutProps
|
|||||||
{/* Main Content Area */}
|
{/* Main Content Area */}
|
||||||
<div className="flex-1 flex flex-col">
|
<div className="flex-1 flex flex-col">
|
||||||
{/* Top Navbar */}
|
{/* Top Navbar */}
|
||||||
<header className="bg-white border-b border-gray-200 px-6 py-4">
|
<header className="bg-white border-b border-gray-200 px-4 sm:px-6 py-3 sm:py-4">
|
||||||
<div className="flex items-center justify-between">
|
<div className="flex items-center justify-between">
|
||||||
<h1 className="text-xl font-semibold text-gray-900"></h1>
|
{/* Hamburger menu button */}
|
||||||
<div className="flex items-center gap-3">
|
<button
|
||||||
|
onClick={() => setSidebarOpen(true)}
|
||||||
|
className="lg:hidden p-2 rounded-lg hover:bg-gray-100 transition-colors"
|
||||||
|
>
|
||||||
|
<svg className="w-6 h-6 text-gray-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M4 6h16M4 12h16M4 18h16" />
|
||||||
|
</svg>
|
||||||
|
</button>
|
||||||
|
<h1 className="text-xl font-semibold text-gray-900 hidden lg:block"></h1>
|
||||||
|
<div className="flex items-center gap-1.5 sm:gap-3">
|
||||||
{/* Notification Button */}
|
{/* Notification Button */}
|
||||||
<div className="relative">
|
<div className="relative">
|
||||||
<button
|
<button
|
||||||
onClick={() => setShowNotifications(!showNotifications)}
|
onClick={() => setShowNotifications(!showNotifications)}
|
||||||
className="relative w-10 h-10 rounded-lg bg-gray-50 hover:bg-gray-100 border border-gray-200 flex items-center justify-center transition-all duration-200 hover:shadow-sm group"
|
className="relative w-8 h-8 sm:w-10 sm:h-10 rounded-lg bg-gray-50 hover:bg-gray-100 border border-gray-200 flex items-center justify-center transition-all duration-200 hover:shadow-sm group"
|
||||||
|
aria-label={`Notifications${unreadNotificationsCount > 0 ? ` (${unreadNotificationsCount} non lues)` : ''}`}
|
||||||
>
|
>
|
||||||
<svg className="w-5 h-5 text-gray-600 group-hover:text-gray-900 transition-colors" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
<svg className="w-4 h-4 sm:w-5 sm:h-5 text-gray-600 group-hover:text-gray-900 transition-colors" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M15 17h5l-1.405-1.405A2.032 2.032 0 0118 14.158V11a6.002 6.002 0 00-4-5.659V5a2 2 0 10-4 0v.341C7.67 6.165 6 8.388 6 11v3.159c0 .538-.214 1.055-.595 1.436L4 17h5m6 0v1a3 3 0 11-6 0v-1m6 0H9" />
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M15 17h5l-1.405-1.405A2.032 2.032 0 0118 14.158V11a6.002 6.002 0 00-4-5.659V5a2 2 0 10-4 0v.341C7.67 6.165 6 8.388 6 11v3.159c0 .538-.214 1.055-.595 1.436L4 17h5m6 0v1a3 3 0 11-6 0v-1m6 0H9" />
|
||||||
</svg>
|
</svg>
|
||||||
{/* Badge de notification */}
|
{/* Badge de notification */}
|
||||||
{unreadNotificationsCount > 0 && (
|
{unreadNotificationsCount > 0 && (
|
||||||
<span className="absolute -top-1 -right-1 min-w-[18px] h-[18px] bg-red-500 text-white text-[10px] font-semibold rounded-full border-2 border-white flex items-center justify-center px-1">
|
<span className="absolute -top-0.5 -right-0.5 min-w-[16px] h-4 sm:min-w-[18px] sm:h-[18px] bg-red-500 text-white text-[9px] sm:text-[10px] font-semibold rounded-full border-2 border-white flex items-center justify-center px-0.5 sm:px-1">
|
||||||
{unreadNotificationsCount > 99 ? '99+' : unreadNotificationsCount}
|
{unreadNotificationsCount > 99 ? '99+' : unreadNotificationsCount}
|
||||||
</span>
|
</span>
|
||||||
)}
|
)}
|
||||||
@@ -284,9 +316,9 @@ export default function DashboardLayout({ user, children }: DashboardLayoutProps
|
|||||||
|
|
||||||
{/* Dropdown Notifications */}
|
{/* Dropdown Notifications */}
|
||||||
{showNotifications && (
|
{showNotifications && (
|
||||||
<div className="absolute right-0 top-12 w-80 bg-white rounded-lg shadow-xl border border-gray-200 z-50 animate-slideUp">
|
<div className="fixed sm:absolute right-2 sm:right-0 top-14 sm:top-12 w-[calc(100vw-1rem)] sm:w-80 max-w-sm bg-white rounded-lg shadow-xl border border-gray-200 z-50 animate-slideUp max-h-[calc(100vh-4rem)] sm:max-h-96 flex flex-col">
|
||||||
<div className="p-4 border-b border-gray-200 flex items-center justify-between">
|
<div className="p-3 sm:p-4 border-b border-gray-200 flex items-center justify-between flex-shrink-0">
|
||||||
<h3 className="text-sm font-semibold text-gray-900">Notifications</h3>
|
<h3 className="text-xs sm:text-sm font-semibold text-gray-900">Notifications</h3>
|
||||||
{unreadNotificationsCount > 0 && (
|
{unreadNotificationsCount > 0 && (
|
||||||
<button
|
<button
|
||||||
onClick={async () => {
|
onClick={async () => {
|
||||||
@@ -303,15 +335,15 @@ export default function DashboardLayout({ user, children }: DashboardLayoutProps
|
|||||||
console.error('Erreur lors du marquage des notifications:', error);
|
console.error('Erreur lors du marquage des notifications:', error);
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
className="text-xs text-lblue hover:text-dblue font-medium"
|
className="text-[10px] sm:text-xs text-lblue hover:text-dblue font-medium"
|
||||||
>
|
>
|
||||||
Tout marquer comme lu
|
Tout marquer comme lu
|
||||||
</button>
|
</button>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
<div className="max-h-96 overflow-y-auto">
|
<div className="overflow-y-auto flex-1 min-h-0">
|
||||||
{notifications.length === 0 ? (
|
{notifications.length === 0 ? (
|
||||||
<div className="p-4 text-center text-sm text-gray-500">
|
<div className="p-4 text-center text-xs sm:text-sm text-gray-500">
|
||||||
Aucune notification
|
Aucune notification
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
@@ -341,22 +373,22 @@ export default function DashboardLayout({ user, children }: DashboardLayoutProps
|
|||||||
setShowNotifications(false);
|
setShowNotifications(false);
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
className={`w-full text-left p-4 hover:bg-gray-50 transition-colors ${
|
className={`w-full text-left p-3 sm:p-4 hover:bg-gray-50 transition-colors active:bg-gray-100 ${
|
||||||
!notification.read ? 'bg-blue-50/50' : ''
|
!notification.read ? 'bg-blue-50/50' : ''
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
<div className="flex items-start gap-3">
|
<div className="flex items-start gap-2 sm:gap-3">
|
||||||
<div className={`flex-shrink-0 w-2 h-2 rounded-full mt-2 ${
|
<div className={`flex-shrink-0 w-1.5 h-1.5 sm:w-2 sm:h-2 rounded-full mt-1.5 sm:mt-2 ${
|
||||||
!notification.read ? 'bg-lblue' : 'bg-transparent'
|
!notification.read ? 'bg-lblue' : 'bg-transparent'
|
||||||
}`}></div>
|
}`}></div>
|
||||||
<div className="flex-1 min-w-0">
|
<div className="flex-1 min-w-0">
|
||||||
<div className="flex items-center justify-between mb-1">
|
<div className="flex items-start sm:items-center justify-between gap-2 mb-1">
|
||||||
<p className={`text-sm font-medium ${
|
<p className={`text-xs sm:text-sm font-medium flex-1 ${
|
||||||
!notification.read ? 'text-gray-900' : 'text-gray-700'
|
!notification.read ? 'text-gray-900' : 'text-gray-700'
|
||||||
}`}>
|
}`}>
|
||||||
{notification.title}
|
{notification.title}
|
||||||
</p>
|
</p>
|
||||||
<span className="text-xs text-gray-400 ml-2 flex-shrink-0">
|
<span className="text-[10px] sm:text-xs text-gray-400 flex-shrink-0 whitespace-nowrap">
|
||||||
{new Date(notification.createdAt).toLocaleDateString('fr-FR', {
|
{new Date(notification.createdAt).toLocaleDateString('fr-FR', {
|
||||||
day: 'numeric',
|
day: 'numeric',
|
||||||
month: 'short',
|
month: 'short',
|
||||||
@@ -365,7 +397,7 @@ export default function DashboardLayout({ user, children }: DashboardLayoutProps
|
|||||||
})}
|
})}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<p className="text-xs text-gray-600 line-clamp-2">
|
<p className="text-[11px] sm:text-xs text-gray-600 line-clamp-2 sm:line-clamp-3">
|
||||||
{notification.message}
|
{notification.message}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
@@ -383,31 +415,31 @@ export default function DashboardLayout({ user, children }: DashboardLayoutProps
|
|||||||
<div className="relative">
|
<div className="relative">
|
||||||
<button
|
<button
|
||||||
onClick={() => setShowProfileMenu(!showProfileMenu)}
|
onClick={() => setShowProfileMenu(!showProfileMenu)}
|
||||||
className="flex items-center gap-3 px-3 py-2 rounded-lg hover:bg-gray-50 border border-gray-200 transition-all duration-200 hover:shadow-sm group"
|
className="flex items-center gap-2 sm:gap-3 px-2 sm:px-3 py-2 rounded-lg hover:bg-gray-50 border border-gray-200 transition-all duration-200 hover:shadow-sm group"
|
||||||
>
|
>
|
||||||
{userPhotoUrl ? (
|
{userPhotoUrl ? (
|
||||||
<img
|
<img
|
||||||
src={userPhotoUrl}
|
src={userPhotoUrl}
|
||||||
alt={user.name || 'Utilisateur'}
|
alt={user.name || 'Utilisateur'}
|
||||||
className="w-9 h-9 rounded-lg object-cover shadow-sm border border-gray-200"
|
className="w-8 h-8 sm:w-9 sm:h-9 rounded-lg object-cover shadow-sm border border-gray-200"
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
<div className="w-9 h-9 rounded-lg bg-gradient-to-br from-lblue to-dblue flex items-center justify-center shadow-sm">
|
<div className="w-8 h-8 sm:w-9 sm:h-9 rounded-lg bg-gradient-to-br from-lblue to-dblue flex items-center justify-center shadow-sm">
|
||||||
<span className="text-white text-sm font-semibold">{getUserInitials()}</span>
|
<span className="text-white text-xs sm:text-sm font-semibold">{getUserInitials()}</span>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
<div className="hidden md:block text-left">
|
<div className="hidden lg:block text-left">
|
||||||
<div className="text-sm font-medium text-gray-900">{user.name || 'Utilisateur'}</div>
|
<div className="text-sm font-medium text-gray-900">{user.name || 'Utilisateur'}</div>
|
||||||
<div className="text-xs text-gray-500">{user.email}</div>
|
<div className="text-xs text-gray-500">{user.email}</div>
|
||||||
</div>
|
</div>
|
||||||
<svg className="w-4 h-4 text-gray-400 group-hover:text-gray-600 transition-colors" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
<svg className="hidden sm:block w-4 h-4 text-gray-400 group-hover:text-gray-600 transition-colors" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M19 9l-7 7-7-7" />
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M19 9l-7 7-7-7" />
|
||||||
</svg>
|
</svg>
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
{/* Dropdown Profile Menu */}
|
{/* Dropdown Profile Menu */}
|
||||||
{showProfileMenu && (
|
{showProfileMenu && (
|
||||||
<div className="absolute right-0 top-14 w-56 bg-white rounded-lg shadow-xl border border-gray-200 z-50 animate-slideUp">
|
<div className="absolute right-0 top-14 w-56 sm:w-64 bg-white rounded-lg shadow-xl border border-gray-200 z-50 animate-slideUp">
|
||||||
<div className="p-4 border-b border-gray-200">
|
<div className="p-4 border-b border-gray-200">
|
||||||
<div className="text-sm font-medium text-gray-900">{user.name || 'Utilisateur'}</div>
|
<div className="text-sm font-medium text-gray-900">{user.name || 'Utilisateur'}</div>
|
||||||
<div className="text-xs text-gray-500 mt-1">{user.email}</div>
|
<div className="text-xs text-gray-500 mt-1">{user.email}</div>
|
||||||
@@ -454,7 +486,7 @@ export default function DashboardLayout({ user, children }: DashboardLayoutProps
|
|||||||
{/* Overlay pour fermer les menus */}
|
{/* Overlay pour fermer les menus */}
|
||||||
{(showNotifications || showProfileMenu) && (
|
{(showNotifications || showProfileMenu) && (
|
||||||
<div
|
<div
|
||||||
className="fixed inset-0 z-40"
|
className="fixed inset-0 z-40 lg:z-30"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setShowNotifications(false);
|
setShowNotifications(false);
|
||||||
setShowProfileMenu(false);
|
setShowProfileMenu(false);
|
||||||
|
|||||||
@@ -118,14 +118,14 @@ export default function ListeTrajets({ onTrajetCreated }: ListeTrajetsProps) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="bg-white rounded-lg shadow-sm">
|
<div className="bg-white rounded-lg shadow-sm overflow-hidden">
|
||||||
{/* Bloc d'actions */}
|
{/* Bloc d'actions */}
|
||||||
<div className="p-6 border-b border-gray-200">
|
<div className="p-4 sm:p-6 border-b border-gray-200">
|
||||||
<div className="flex flex-col gap-4">
|
<div className="flex flex-col gap-3 sm:gap-4">
|
||||||
{/* Barre de recherche */}
|
{/* Barre de recherche */}
|
||||||
<div className="relative">
|
<div className="relative">
|
||||||
<div className="absolute inset-y-0 left-0 pl-3 flex items-center pointer-events-none">
|
<div className="absolute inset-y-0 left-0 pl-3 flex items-center pointer-events-none">
|
||||||
<svg className="h-4 w-4 text-gray-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
<svg className="h-4 w-4 sm:h-5 sm:w-5 text-gray-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z" />
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z" />
|
||||||
</svg>
|
</svg>
|
||||||
</div>
|
</div>
|
||||||
@@ -134,7 +134,7 @@ export default function ListeTrajets({ onTrajetCreated }: ListeTrajetsProps) {
|
|||||||
placeholder="Rechercher un trajet..."
|
placeholder="Rechercher un trajet..."
|
||||||
value={search}
|
value={search}
|
||||||
onChange={(e) => setSearch(e.target.value)}
|
onChange={(e) => setSearch(e.target.value)}
|
||||||
className="block w-full pl-10 pr-3 py-2.5 text-sm border border-gray-300 rounded-lg text-gray-900 placeholder-gray-400 focus:outline-none focus:ring-2 focus:ring-lblue focus:border-transparent"
|
className="block w-full pl-10 sm:pl-12 pr-4 py-3 text-base 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 max-w-full"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -142,35 +142,36 @@ export default function ListeTrajets({ onTrajetCreated }: ListeTrajetsProps) {
|
|||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
<button
|
<button
|
||||||
onClick={() => setShowTrajetForm(true)}
|
onClick={() => setShowTrajetForm(true)}
|
||||||
className="flex-1 flex items-center justify-center gap-2 px-4 py-2.5 bg-lgreen text-white text-sm font-medium rounded-lg hover:bg-dgreen transition-colors"
|
className="flex-1 flex items-center justify-center gap-1.5 sm:gap-2 px-3 sm:px-4 py-2 sm:py-2.5 bg-lgreen text-white text-xs sm:text-sm font-medium rounded-lg hover:bg-dgreen transition-colors"
|
||||||
>
|
>
|
||||||
<svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
<svg className="w-4 h-4 sm:w-5 sm:h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M12 4v16m8-8H4" />
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M12 4v16m8-8H4" />
|
||||||
</svg>
|
</svg>
|
||||||
Nouveau trajet
|
<span className="hidden sm:inline">Nouveau trajet</span>
|
||||||
|
<span className="sm:hidden">Nouveau</span>
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
onClick={() => setShowFilters(!showFilters)}
|
onClick={() => setShowFilters(!showFilters)}
|
||||||
className={`px-4 py-2.5 text-sm font-medium rounded-lg transition-colors flex items-center gap-2 ${
|
className={`px-3 sm:px-4 py-2 sm:py-2.5 text-xs sm:text-sm font-medium rounded-lg transition-colors flex items-center gap-1.5 sm:gap-2 ${
|
||||||
showFilters || filterStatut || startDate || endDate
|
showFilters || filterStatut || startDate || endDate
|
||||||
? 'bg-lblue text-white hover:bg-dblue'
|
? 'bg-lblue text-white hover:bg-dblue'
|
||||||
: 'bg-gray-100 text-gray-700 hover:bg-gray-200'
|
: 'bg-gray-100 text-gray-700 hover:bg-gray-200'
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
<svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
<svg className="w-4 h-4 sm:w-5 sm:h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M3 4a1 1 0 011-1h16a1 1 0 011 1v2.586a1 1 0 01-.293.707l-6.414 6.414a1 1 0 00-.293.707V17l-4 4v-6.586a1 1 0 00-.293-.707L3.293 7.293A1 1 0 013 6.586V4z" />
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M3 4a1 1 0 011-1h16a1 1 0 011 1v2.586a1 1 0 01-.293.707l-6.414 6.414a1 1 0 00-.293.707V17l-4 4v-6.586a1 1 0 00-.293-.707L3.293 7.293A1 1 0 013 6.586V4z" />
|
||||||
</svg>
|
</svg>
|
||||||
Filtrer
|
<span className="hidden sm:inline">Filtrer</span>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Filtres */}
|
{/* Filtres */}
|
||||||
{showFilters && (
|
{showFilters && (
|
||||||
<div className="pt-3 border-t border-gray-200 space-y-4">
|
<div className="pt-3 border-t border-gray-200 space-y-3 sm:space-y-4">
|
||||||
{/* Filtre par plage de dates */}
|
{/* Filtre par plage de dates */}
|
||||||
<div>
|
<div>
|
||||||
<div className="flex items-center justify-between mb-2">
|
<div className="flex items-center justify-between mb-2">
|
||||||
<label className="block text-sm font-medium text-gray-700">
|
<label className="block text-xs sm:text-sm font-medium text-gray-700">
|
||||||
Plage de dates
|
Plage de dates
|
||||||
</label>
|
</label>
|
||||||
{(startDate || endDate) && (
|
{(startDate || endDate) && (
|
||||||
@@ -179,33 +180,33 @@ export default function ListeTrajets({ onTrajetCreated }: ListeTrajetsProps) {
|
|||||||
setStartDate('');
|
setStartDate('');
|
||||||
setEndDate('');
|
setEndDate('');
|
||||||
}}
|
}}
|
||||||
className="text-xs font-medium text-gray-600 hover:text-gray-900 transition-colors flex items-center gap-1"
|
className="text-[10px] sm:text-xs font-medium text-gray-600 hover:text-gray-900 transition-colors flex items-center gap-1"
|
||||||
>
|
>
|
||||||
<svg className="w-3 h-3" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
<svg className="w-2.5 h-2.5 sm:w-3 sm:h-3" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M6 18L18 6M6 6l12 12" />
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M6 18L18 6M6 6l12 12" />
|
||||||
</svg>
|
</svg>
|
||||||
Réinitialiser
|
Réinitialiser
|
||||||
</button>
|
</button>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
<div className="grid grid-cols-1 sm:grid-cols-2 gap-3">
|
<div className="grid grid-cols-1 sm:grid-cols-2 gap-2 sm:gap-3">
|
||||||
<div>
|
<div className="min-w-0">
|
||||||
<label className="block text-xs text-gray-500 mb-1">Date de début</label>
|
<label className="block text-xs font-medium text-gray-700 mb-1.5">Date de début</label>
|
||||||
<input
|
<input
|
||||||
type="date"
|
type="date"
|
||||||
value={startDate}
|
value={startDate}
|
||||||
onChange={(e) => setStartDate(e.target.value)}
|
onChange={(e) => setStartDate(e.target.value)}
|
||||||
className="block w-full px-3 py-2 text-sm border border-gray-300 rounded-lg text-gray-900 focus:outline-none focus:ring-2 focus:ring-lblue focus:border-transparent"
|
className="block w-full px-3 py-2 text-sm sm:text-base border border-gray-300 rounded-lg bg-white text-gray-900 focus:outline-none focus:ring-2 focus:ring-lblue focus:border-lblue transition-all max-w-full box-border"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div className="min-w-0">
|
||||||
<label className="block text-xs text-gray-500 mb-1">Date de fin</label>
|
<label className="block text-xs font-medium text-gray-700 mb-1.5">Date de fin</label>
|
||||||
<input
|
<input
|
||||||
type="date"
|
type="date"
|
||||||
value={endDate}
|
value={endDate}
|
||||||
onChange={(e) => setEndDate(e.target.value)}
|
onChange={(e) => setEndDate(e.target.value)}
|
||||||
min={startDate || undefined}
|
min={startDate || undefined}
|
||||||
className="block w-full px-3 py-2 text-sm border border-gray-300 rounded-lg text-gray-900 focus:outline-none focus:ring-2 focus:ring-lblue focus:border-transparent"
|
className="block w-full px-3 py-2 text-sm sm:text-base border border-gray-300 rounded-lg bg-white text-gray-900 focus:outline-none focus:ring-2 focus:ring-lblue focus:border-lblue transition-all max-w-full box-border"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -213,13 +214,13 @@ export default function ListeTrajets({ onTrajetCreated }: ListeTrajetsProps) {
|
|||||||
|
|
||||||
{/* Filtre par statut */}
|
{/* Filtre par statut */}
|
||||||
<div>
|
<div>
|
||||||
<label className="block text-sm font-medium text-gray-700 mb-2">
|
<label className="block text-xs sm:text-sm font-medium text-gray-700 mb-2">
|
||||||
Statut
|
Statut
|
||||||
</label>
|
</label>
|
||||||
<div className="flex flex-wrap gap-2">
|
<div className="flex flex-wrap gap-1.5 sm:gap-2">
|
||||||
<button
|
<button
|
||||||
onClick={() => setFilterStatut('')}
|
onClick={() => setFilterStatut('')}
|
||||||
className={`px-3 py-2 text-xs font-medium rounded transition-colors ${
|
className={`px-2 sm:px-3 py-1.5 sm:py-2 text-[10px] sm:text-xs font-medium rounded transition-colors ${
|
||||||
!filterStatut
|
!filterStatut
|
||||||
? 'bg-lblue text-white'
|
? 'bg-lblue text-white'
|
||||||
: 'bg-gray-100 text-gray-700 hover:bg-gray-200'
|
: 'bg-gray-100 text-gray-700 hover:bg-gray-200'
|
||||||
@@ -229,7 +230,7 @@ export default function ListeTrajets({ onTrajetCreated }: ListeTrajetsProps) {
|
|||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
onClick={() => setFilterStatut('Planifié')}
|
onClick={() => setFilterStatut('Planifié')}
|
||||||
className={`px-3 py-2 text-xs font-medium rounded transition-colors ${
|
className={`px-2 sm:px-3 py-1.5 sm:py-2 text-[10px] sm:text-xs font-medium rounded transition-colors ${
|
||||||
filterStatut === 'Planifié'
|
filterStatut === 'Planifié'
|
||||||
? 'bg-lblue text-white'
|
? 'bg-lblue text-white'
|
||||||
: 'bg-gray-100 text-gray-700 hover:bg-gray-200'
|
: 'bg-gray-100 text-gray-700 hover:bg-gray-200'
|
||||||
@@ -239,7 +240,7 @@ export default function ListeTrajets({ onTrajetCreated }: ListeTrajetsProps) {
|
|||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
onClick={() => setFilterStatut('En cours')}
|
onClick={() => setFilterStatut('En cours')}
|
||||||
className={`px-3 py-2 text-xs font-medium rounded transition-colors ${
|
className={`px-2 sm:px-3 py-1.5 sm:py-2 text-[10px] sm:text-xs font-medium rounded transition-colors ${
|
||||||
filterStatut === 'En cours'
|
filterStatut === 'En cours'
|
||||||
? 'bg-lblue text-white'
|
? 'bg-lblue text-white'
|
||||||
: 'bg-gray-100 text-gray-700 hover:bg-gray-200'
|
: 'bg-gray-100 text-gray-700 hover:bg-gray-200'
|
||||||
@@ -249,7 +250,7 @@ export default function ListeTrajets({ onTrajetCreated }: ListeTrajetsProps) {
|
|||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
onClick={() => setFilterStatut('Terminé')}
|
onClick={() => setFilterStatut('Terminé')}
|
||||||
className={`px-3 py-2 text-xs font-medium rounded transition-colors ${
|
className={`px-2 sm:px-3 py-1.5 sm:py-2 text-[10px] sm:text-xs font-medium rounded transition-colors ${
|
||||||
filterStatut === 'Terminé'
|
filterStatut === 'Terminé'
|
||||||
? 'bg-lblue text-white'
|
? 'bg-lblue text-white'
|
||||||
: 'bg-gray-100 text-gray-700 hover:bg-gray-200'
|
: 'bg-gray-100 text-gray-700 hover:bg-gray-200'
|
||||||
@@ -259,7 +260,7 @@ export default function ListeTrajets({ onTrajetCreated }: ListeTrajetsProps) {
|
|||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
onClick={() => setFilterStatut('Annulé')}
|
onClick={() => setFilterStatut('Annulé')}
|
||||||
className={`px-3 py-2 text-xs font-medium rounded transition-colors ${
|
className={`px-2 sm:px-3 py-1.5 sm:py-2 text-[10px] sm:text-xs font-medium rounded transition-colors ${
|
||||||
filterStatut === 'Annulé'
|
filterStatut === 'Annulé'
|
||||||
? 'bg-lblue text-white'
|
? 'bg-lblue text-white'
|
||||||
: 'bg-gray-100 text-gray-700 hover:bg-gray-200'
|
: 'bg-gray-100 text-gray-700 hover:bg-gray-200'
|
||||||
@@ -275,65 +276,65 @@ export default function ListeTrajets({ onTrajetCreated }: ListeTrajetsProps) {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Liste des trajets */}
|
{/* Liste des trajets */}
|
||||||
<div className="p-6">
|
<div className="p-4 sm:p-6">
|
||||||
<div className="flex items-center justify-between mb-6">
|
<div className="flex items-center justify-between mb-4 sm:mb-6">
|
||||||
<h2 className="text-xl font-semibold text-gray-900">
|
<h2 className="text-lg sm:text-xl font-semibold text-gray-900">
|
||||||
Derniers trajets créés
|
Derniers trajets créés
|
||||||
</h2>
|
</h2>
|
||||||
<button
|
<button
|
||||||
onClick={fetchTrajets}
|
onClick={fetchTrajets}
|
||||||
className="text-sm text-lblue hover:text-dblue font-medium flex items-center gap-2"
|
className="text-xs sm:text-sm text-lblue hover:text-dblue font-medium flex items-center gap-1 sm:gap-2"
|
||||||
>
|
>
|
||||||
<svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
<svg className="w-3.5 h-3.5 sm:w-4 sm:h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15" />
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15" />
|
||||||
</svg>
|
</svg>
|
||||||
Actualiser
|
<span className="hidden sm:inline">Actualiser</span>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{loading ? (
|
{loading ? (
|
||||||
<div className="text-center py-8 text-gray-500">Chargement...</div>
|
<div className="text-center py-6 sm:py-8 text-sm text-gray-500">Chargement...</div>
|
||||||
) : filteredTrajets.length === 0 ? (
|
) : filteredTrajets.length === 0 ? (
|
||||||
<div className="text-center py-8 text-gray-500">
|
<div className="text-center py-6 sm:py-8 text-xs sm:text-sm text-gray-500">
|
||||||
{trajets.length === 0
|
{trajets.length === 0
|
||||||
? 'Aucun trajet créé récemment'
|
? 'Aucun trajet créé récemment'
|
||||||
: 'Aucun trajet ne correspond à votre recherche'}
|
: 'Aucun trajet ne correspond à votre recherche'}
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<div className="space-y-3">
|
<div className="space-y-2 sm:space-y-3">
|
||||||
{filteredTrajets.map((trajet) => (
|
{filteredTrajets.map((trajet) => (
|
||||||
<div
|
<div
|
||||||
key={trajet.id}
|
key={trajet.id}
|
||||||
className="p-4 bg-gray-50 rounded-lg border border-gray-200 hover:border-gray-300 transition-colors"
|
className="p-3 sm:p-4 bg-gray-50 rounded-lg border border-gray-200 hover:border-gray-300 transition-colors"
|
||||||
>
|
>
|
||||||
<div className="flex items-start gap-4">
|
<div className="flex items-start gap-3 sm:gap-4">
|
||||||
{/* Avatar adhérent */}
|
{/* Avatar adhérent */}
|
||||||
<div className="w-12 h-12 rounded-full bg-lgreen flex items-center justify-center text-white text-sm font-semibold flex-shrink-0">
|
<div className="w-10 h-10 sm:w-12 sm:h-12 rounded-full bg-lgreen flex items-center justify-center text-white text-xs sm:text-sm font-semibold flex-shrink-0">
|
||||||
{getInitials(trajet.adherent.nom, trajet.adherent.prenom)}
|
{getInitials(trajet.adherent.nom, trajet.adherent.prenom)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Informations principales */}
|
{/* Informations principales */}
|
||||||
<div className="flex-1 min-w-0">
|
<div className="flex-1 min-w-0">
|
||||||
<div className="flex items-start justify-between gap-4 mb-2">
|
<div className="flex items-start justify-between gap-2 sm:gap-4 mb-2">
|
||||||
<div>
|
<div className="flex-1 min-w-0">
|
||||||
<h3 className="text-sm font-semibold text-gray-900">
|
<h3 className="text-xs sm:text-sm font-semibold text-gray-900 truncate">
|
||||||
{trajet.adherent.prenom} {trajet.adherent.nom}
|
{trajet.adherent.prenom} {trajet.adherent.nom}
|
||||||
</h3>
|
</h3>
|
||||||
<div className="flex items-center gap-3 mt-1">
|
<div className="flex flex-wrap items-center gap-2 sm:gap-3 mt-1">
|
||||||
<span className="text-xs text-gray-500 flex items-center gap-1">
|
<span className="text-[10px] sm:text-xs text-gray-500 flex items-center gap-1">
|
||||||
<svg className="w-3 h-3" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
<svg className="w-2.5 h-2.5 sm:w-3 sm:h-3" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M8 7V3m8 4V3m-9 8h10M5 21h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v12a2 2 0 002 2z" />
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M8 7V3m8 4V3m-9 8h10M5 21h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v12a2 2 0 002 2z" />
|
||||||
</svg>
|
</svg>
|
||||||
{formatDate(trajet.date)}
|
{formatDate(trajet.date)}
|
||||||
</span>
|
</span>
|
||||||
<span className="text-xs text-gray-500 flex items-center gap-1">
|
<span className="text-[10px] sm:text-xs text-gray-500 flex items-center gap-1">
|
||||||
<svg className="w-3 h-3" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
<svg className="w-2.5 h-2.5 sm:w-3 sm:h-3" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M12 8v4l3 3m6-3a9 9 0 11-18 0 9 9 0 0118 0z" />
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M12 8v4l3 3m6-3a9 9 0 11-18 0 9 9 0 0118 0z" />
|
||||||
</svg>
|
</svg>
|
||||||
{formatTime(trajet.date)}
|
{formatTime(trajet.date)}
|
||||||
</span>
|
</span>
|
||||||
<span
|
<span
|
||||||
className={`px-2 py-0.5 text-xs font-medium rounded ${
|
className={`px-1.5 sm:px-2 py-0.5 text-[10px] sm:text-xs font-medium rounded ${
|
||||||
trajet.statut === 'Terminé'
|
trajet.statut === 'Terminé'
|
||||||
? 'bg-green-100 text-green-700'
|
? 'bg-green-100 text-green-700'
|
||||||
: trajet.statut === 'En cours'
|
: trajet.statut === 'En cours'
|
||||||
@@ -351,36 +352,36 @@ export default function ListeTrajets({ onTrajetCreated }: ListeTrajetsProps) {
|
|||||||
|
|
||||||
{/* Adresses */}
|
{/* Adresses */}
|
||||||
<div className="space-y-1 mb-2">
|
<div className="space-y-1 mb-2">
|
||||||
<div className="text-sm text-gray-600">
|
<div className="text-xs sm:text-sm text-gray-600 break-words">
|
||||||
<span className="font-medium">Départ:</span>{' '}
|
<span className="font-medium">Départ:</span>{' '}
|
||||||
<span className="text-gray-900">{trajet.adresseDepart}</span>
|
<span className="text-gray-900">{trajet.adresseDepart}</span>
|
||||||
</div>
|
</div>
|
||||||
<div className="text-sm text-gray-600">
|
<div className="text-xs sm:text-sm text-gray-600 break-words">
|
||||||
<span className="font-medium">Arrivée:</span>{' '}
|
<span className="font-medium">Arrivée:</span>{' '}
|
||||||
<span className="text-gray-900">{trajet.adresseArrivee}</span>
|
<span className="text-gray-900">{trajet.adresseArrivee}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Chauffeur */}
|
{/* Chauffeur */}
|
||||||
<div className="flex items-center gap-4 mt-3">
|
<div className="flex items-center gap-2 sm:gap-4 mt-2 sm:mt-3">
|
||||||
{trajet.chauffeur ? (
|
{trajet.chauffeur ? (
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
<div className="w-8 h-8 rounded-full bg-lblue flex items-center justify-center text-white text-xs font-semibold">
|
<div className="w-7 h-7 sm:w-8 sm:h-8 rounded-full bg-lblue flex items-center justify-center text-white text-[10px] sm:text-xs font-semibold">
|
||||||
{getInitials(trajet.chauffeur.nom, trajet.chauffeur.prenom)}
|
{getInitials(trajet.chauffeur.nom, trajet.chauffeur.prenom)}
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<div className="text-xs text-gray-500">Chauffeur</div>
|
<div className="text-[10px] sm:text-xs text-gray-500">Chauffeur</div>
|
||||||
<div className="text-sm font-medium text-gray-900">
|
<div className="text-xs sm:text-sm font-medium text-gray-900 truncate">
|
||||||
{trajet.chauffeur.prenom} {trajet.chauffeur.nom}
|
{trajet.chauffeur.prenom} {trajet.chauffeur.nom}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-1.5 sm:gap-2">
|
||||||
<svg className="w-5 h-5 text-orange-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
<svg className="w-4 h-4 sm:w-5 sm:h-5 text-orange-400 flex-shrink-0" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z" />
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z" />
|
||||||
</svg>
|
</svg>
|
||||||
<span className="text-sm text-orange-600 font-medium">
|
<span className="text-xs sm:text-sm text-orange-600 font-medium">
|
||||||
Aucun chauffeur assigné
|
Aucun chauffeur assigné
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
@@ -390,7 +391,7 @@ export default function ListeTrajets({ onTrajetCreated }: ListeTrajetsProps) {
|
|||||||
{/* Commentaire */}
|
{/* Commentaire */}
|
||||||
{trajet.commentaire && (
|
{trajet.commentaire && (
|
||||||
<div className="mt-2 pt-2 border-t border-gray-200">
|
<div className="mt-2 pt-2 border-t border-gray-200">
|
||||||
<p className="text-xs text-gray-500 italic">{trajet.commentaire}</p>
|
<p className="text-[10px] sm:text-xs text-gray-500 italic break-words">{trajet.commentaire}</p>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -41,21 +41,21 @@ export default function LoginForm() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<form className="space-y-5" onSubmit={handleSubmit}>
|
<form className="space-y-4 sm:space-y-5" onSubmit={handleSubmit}>
|
||||||
{error && (
|
{error && (
|
||||||
<div className="bg-red-50 border border-red-200 text-red-600 px-4 py-3 rounded-lg text-sm">
|
<div className="bg-red-50 border border-red-200 text-red-600 px-3 sm:px-4 py-2.5 sm:py-3 rounded-lg text-xs sm:text-sm">
|
||||||
{error}
|
{error}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{/* Email Field */}
|
{/* Email Field */}
|
||||||
<div>
|
<div>
|
||||||
<label htmlFor="email" className="block text-sm font-medium text-gray-700 mb-2">
|
<label htmlFor="email" className="block text-xs sm:text-sm font-medium text-gray-700 mb-1.5 sm:mb-2">
|
||||||
Email
|
Email
|
||||||
</label>
|
</label>
|
||||||
<div className="relative">
|
<div className="relative">
|
||||||
<div className="absolute inset-y-0 left-0 pl-3 flex items-center pointer-events-none">
|
<div className="absolute inset-y-0 left-0 pl-3 flex items-center pointer-events-none">
|
||||||
<svg className="h-5 w-5 text-lorange" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
<svg className="h-4 w-4 sm:h-5 sm:w-5 text-lorange" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M3 8l7.89 5.26a2 2 0 002.22 0L21 8M5 19h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v10a2 2 0 002 2z" />
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M3 8l7.89 5.26a2 2 0 002.22 0L21 8M5 19h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v10a2 2 0 002 2z" />
|
||||||
</svg>
|
</svg>
|
||||||
</div>
|
</div>
|
||||||
@@ -65,7 +65,7 @@ export default function LoginForm() {
|
|||||||
type="email"
|
type="email"
|
||||||
autoComplete="email"
|
autoComplete="email"
|
||||||
required
|
required
|
||||||
className="block w-full pl-10 pr-3 py-3 border border-gray-300 rounded-lg placeholder-gray-400 text-gray-900 focus:outline-none focus:ring-2 focus:ring-primary focus:border-transparent sm:text-sm"
|
className="block w-full pl-9 sm:pl-10 pr-3 py-2.5 sm:py-3 border border-gray-300 rounded-lg placeholder-gray-400 text-gray-900 text-base focus:outline-none focus:ring-2 focus:ring-primary focus:border-transparent"
|
||||||
placeholder="admin@example.com"
|
placeholder="admin@example.com"
|
||||||
value={email}
|
value={email}
|
||||||
onChange={(e) => setEmail(e.target.value)}
|
onChange={(e) => setEmail(e.target.value)}
|
||||||
@@ -75,12 +75,12 @@ export default function LoginForm() {
|
|||||||
|
|
||||||
{/* Password Field */}
|
{/* Password Field */}
|
||||||
<div>
|
<div>
|
||||||
<label htmlFor="password" className="block text-sm font-medium text-gray-700 mb-2">
|
<label htmlFor="password" className="block text-xs sm:text-sm font-medium text-gray-700 mb-1.5 sm:mb-2">
|
||||||
Mot de passe
|
Mot de passe
|
||||||
</label>
|
</label>
|
||||||
<div className="relative">
|
<div className="relative">
|
||||||
<div className="absolute inset-y-0 left-0 pl-3 flex items-center pointer-events-none">
|
<div className="absolute inset-y-0 left-0 pl-3 flex items-center pointer-events-none">
|
||||||
<svg className="h-5 w-5 text-lorange" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
<svg className="h-4 w-4 sm:h-5 sm:w-5 text-lorange" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M12 15v2m-6 4h12a2 2 0 002-2v-6a2 2 0 00-2-2H6a2 2 0 00-2 2v6a2 2 0 002 2zm10-10V7a4 4 0 00-8 0v4h8z" />
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M12 15v2m-6 4h12a2 2 0 002-2v-6a2 2 0 00-2-2H6a2 2 0 00-2 2v6a2 2 0 002 2zm10-10V7a4 4 0 00-8 0v4h8z" />
|
||||||
</svg>
|
</svg>
|
||||||
</div>
|
</div>
|
||||||
@@ -90,7 +90,7 @@ export default function LoginForm() {
|
|||||||
type="password"
|
type="password"
|
||||||
autoComplete="current-password"
|
autoComplete="current-password"
|
||||||
required
|
required
|
||||||
className="block w-full pl-10 pr-3 py-3 border border-gray-300 rounded-lg placeholder-gray-400 text-gray-900 focus:outline-none focus:ring-2 focus:ring-primary focus:border-transparent sm:text-sm"
|
className="block w-full pl-9 sm:pl-10 pr-3 py-2.5 sm:py-3 border border-gray-300 rounded-lg placeholder-gray-400 text-gray-900 text-base focus:outline-none focus:ring-2 focus:ring-primary focus:border-transparent"
|
||||||
placeholder="••••••••"
|
placeholder="••••••••"
|
||||||
value={password}
|
value={password}
|
||||||
onChange={(e) => setPassword(e.target.value)}
|
onChange={(e) => setPassword(e.target.value)}
|
||||||
@@ -103,14 +103,14 @@ export default function LoginForm() {
|
|||||||
<button
|
<button
|
||||||
type="submit"
|
type="submit"
|
||||||
disabled={loading}
|
disabled={loading}
|
||||||
className="w-full flex justify-center items-center py-3 px-4 border border-transparent rounded-lg text-sm font-medium text-white bg-lblue hover:bg-dblue focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-lblue disabled:opacity-50 disabled:cursor-not-allowed transition-colors"
|
className="w-full flex justify-center items-center py-2.5 sm:py-3 px-4 border border-transparent rounded-lg text-xs sm:text-sm font-medium text-white bg-lblue hover:bg-dblue focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-lblue disabled:opacity-50 disabled:cursor-not-allowed transition-colors"
|
||||||
>
|
>
|
||||||
{loading ? (
|
{loading ? (
|
||||||
'Connexion...'
|
'Connexion...'
|
||||||
) : (
|
) : (
|
||||||
<>
|
<>
|
||||||
Se connecter
|
Se connecter
|
||||||
<svg className="ml-2 h-4 w-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
<svg className="ml-2 h-3.5 w-3.5 sm:h-4 sm:w-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9 5l7 7-7 7" />
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9 5l7 7-7 7" />
|
||||||
</svg>
|
</svg>
|
||||||
</>
|
</>
|
||||||
|
|||||||
@@ -45,6 +45,7 @@ export default function Messagerie() {
|
|||||||
const [selectedConversation, setSelectedConversation] = useState<string | null>(null);
|
const [selectedConversation, setSelectedConversation] = useState<string | null>(null);
|
||||||
const [showNewConversation, setShowNewConversation] = useState(false);
|
const [showNewConversation, setShowNewConversation] = useState(false);
|
||||||
const [showGroupSettings, setShowGroupSettings] = useState(false);
|
const [showGroupSettings, setShowGroupSettings] = useState(false);
|
||||||
|
const [showSidebar, setShowSidebar] = useState(false);
|
||||||
|
|
||||||
const { data: conversations, error, mutate } = useSWR<Conversation[]>(
|
const { data: conversations, error, mutate } = useSWR<Conversation[]>(
|
||||||
'/api/conversations',
|
'/api/conversations',
|
||||||
@@ -69,11 +70,22 @@ export default function Messagerie() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex h-[calc(100vh-80px)] bg-white">
|
<div className="flex h-[calc(100vh-80px)] bg-white relative">
|
||||||
|
{/* Overlay pour mobile */}
|
||||||
|
{showSidebar && (
|
||||||
|
<div
|
||||||
|
className="fixed inset-0 bg-black/50 z-40 md:hidden"
|
||||||
|
onClick={() => setShowSidebar(false)}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
|
||||||
{/* Liste des conversations */}
|
{/* Liste des conversations */}
|
||||||
<div className="w-80 border-r border-gray-200 flex flex-col">
|
<div className={`absolute md:relative w-80 border-r border-gray-200 flex flex-col bg-white z-50 md:z-auto h-full transition-transform duration-300 ${
|
||||||
|
showSidebar ? 'translate-x-0' : '-translate-x-full md:translate-x-0'
|
||||||
|
}`}>
|
||||||
<div className="p-4 border-b border-gray-200 flex items-center justify-between">
|
<div className="p-4 border-b border-gray-200 flex items-center justify-between">
|
||||||
<h2 className="text-lg font-semibold text-gray-900">Messages</h2>
|
<h2 className="text-lg font-semibold text-gray-900">Messages</h2>
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
<button
|
<button
|
||||||
onClick={() => setShowNewConversation(true)}
|
onClick={() => setShowNewConversation(true)}
|
||||||
className="p-2 rounded-lg bg-lblue text-white hover:bg-dblue transition-colors"
|
className="p-2 rounded-lg bg-lblue text-white hover:bg-dblue transition-colors"
|
||||||
@@ -83,6 +95,16 @@ export default function Messagerie() {
|
|||||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M12 4v16m8-8H4" />
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M12 4v16m8-8H4" />
|
||||||
</svg>
|
</svg>
|
||||||
</button>
|
</button>
|
||||||
|
<button
|
||||||
|
onClick={() => setShowSidebar(false)}
|
||||||
|
className="p-2 rounded-lg hover:bg-gray-100 transition-colors md:hidden"
|
||||||
|
title="Fermer"
|
||||||
|
>
|
||||||
|
<svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M6 18L18 6M6 6l12 12" />
|
||||||
|
</svg>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="flex-1 overflow-y-auto">
|
<div className="flex-1 overflow-y-auto">
|
||||||
@@ -106,6 +128,7 @@ export default function Messagerie() {
|
|||||||
key={conversation.id}
|
key={conversation.id}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setSelectedConversation(conversation.id);
|
setSelectedConversation(conversation.id);
|
||||||
|
setShowSidebar(false); // Fermer la sidebar sur mobile après sélection
|
||||||
if (conversation.type === 'group') {
|
if (conversation.type === 'group') {
|
||||||
setShowGroupSettings(false);
|
setShowGroupSettings(false);
|
||||||
}
|
}
|
||||||
@@ -149,7 +172,7 @@ export default function Messagerie() {
|
|||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
{conversation.lastMessage ? (
|
{conversation.lastMessage ? (
|
||||||
<p className="text-sm text-gray-600 truncate">
|
<p className="text-xs md:text-sm text-gray-600 truncate">
|
||||||
{conversation.type === 'group' && conversation.lastMessage.senderName
|
{conversation.type === 'group' && conversation.lastMessage.senderName
|
||||||
? `${conversation.lastMessage.senderName}: `
|
? `${conversation.lastMessage.senderName}: `
|
||||||
: ''}
|
: ''}
|
||||||
@@ -158,7 +181,7 @@ export default function Messagerie() {
|
|||||||
: conversation.lastMessage.content || '📎 Fichier'}
|
: conversation.lastMessage.content || '📎 Fichier'}
|
||||||
</p>
|
</p>
|
||||||
) : (
|
) : (
|
||||||
<p className="text-sm text-gray-400 italic">Aucun message</p>
|
<p className="text-xs md:text-sm text-gray-400 italic">Aucun message</p>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -170,17 +193,18 @@ export default function Messagerie() {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Fenêtre de chat */}
|
{/* Fenêtre de chat */}
|
||||||
<div className="flex-1 flex flex-col">
|
<div className="flex-1 flex flex-col w-full">
|
||||||
{selectedConversation && selectedConv ? (
|
{selectedConversation && selectedConv ? (
|
||||||
<ChatWindow
|
<ChatWindow
|
||||||
conversationId={selectedConversation}
|
conversationId={selectedConversation}
|
||||||
conversation={selectedConv}
|
conversation={selectedConv}
|
||||||
onNewMessage={handleNewMessage}
|
onNewMessage={handleNewMessage}
|
||||||
onShowGroupSettings={() => setShowGroupSettings(true)}
|
onShowGroupSettings={() => setShowGroupSettings(true)}
|
||||||
|
onShowSidebar={() => setShowSidebar(true)}
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
<div className="flex-1 flex items-center justify-center bg-gray-50">
|
<div className="flex-1 flex items-center justify-center bg-gray-50">
|
||||||
<div className="text-center">
|
<div className="text-center p-4">
|
||||||
<svg
|
<svg
|
||||||
className="w-16 h-16 text-gray-400 mx-auto mb-4"
|
className="w-16 h-16 text-gray-400 mx-auto mb-4"
|
||||||
fill="none"
|
fill="none"
|
||||||
@@ -194,7 +218,13 @@ export default function Messagerie() {
|
|||||||
d="M8 12h.01M12 12h.01M16 12h.01M21 12c0 4.418-4.03 8-9 8a9.863 9.863 0 01-4.255-.949L3 20l1.395-3.72C3.512 15.042 3 13.574 3 12c0-4.418 4.03-8 9-8s9 3.582 9 8z"
|
d="M8 12h.01M12 12h.01M16 12h.01M21 12c0 4.418-4.03 8-9 8a9.863 9.863 0 01-4.255-.949L3 20l1.395-3.72C3.512 15.042 3 13.574 3 12c0-4.418 4.03-8 9-8s9 3.582 9 8z"
|
||||||
/>
|
/>
|
||||||
</svg>
|
</svg>
|
||||||
<p className="text-gray-500">Sélectionnez une conversation pour commencer</p>
|
<p className="text-gray-500 mb-4">Sélectionnez une conversation pour commencer</p>
|
||||||
|
<button
|
||||||
|
onClick={() => setShowSidebar(true)}
|
||||||
|
className="md:hidden px-4 py-2 bg-lblue text-white rounded-lg hover:bg-dblue transition-colors"
|
||||||
|
>
|
||||||
|
Voir les conversations
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -2,8 +2,7 @@
|
|||||||
|
|
||||||
import { useState, useEffect } from 'react';
|
import { useState, useEffect } from 'react';
|
||||||
import { useRouter } from 'next/navigation';
|
import { useRouter } from 'next/navigation';
|
||||||
import { useNotification } from './NotificationProvider';
|
import AlertModal from './AlertModal';
|
||||||
import useSWR from 'swr';
|
|
||||||
|
|
||||||
interface UserProfile {
|
interface UserProfile {
|
||||||
id: string;
|
id: string;
|
||||||
@@ -17,61 +16,53 @@ interface UserProfile {
|
|||||||
} | null;
|
} | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
const fetcher = (url: string) => fetch(url).then((res) => res.json());
|
|
||||||
|
|
||||||
export default function ModifierCompteContent() {
|
export default function ModifierCompteContent() {
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const { showNotification } = useNotification();
|
|
||||||
const { data: userProfile, mutate } = useSWR<UserProfile>('/api/user/profile', fetcher);
|
|
||||||
|
|
||||||
const [loading, setLoading] = useState(false);
|
const [loading, setLoading] = useState(false);
|
||||||
|
const [loadingProfile, setLoadingProfile] = useState(true);
|
||||||
|
const [alertModal, setAlertModal] = useState<{
|
||||||
|
show: boolean;
|
||||||
|
type: 'success' | 'error' | 'info' | 'warning';
|
||||||
|
title: string;
|
||||||
|
message: string;
|
||||||
|
} | null>(null);
|
||||||
const [formData, setFormData] = useState({
|
const [formData, setFormData] = useState({
|
||||||
name: '',
|
name: '',
|
||||||
email: '',
|
email: '',
|
||||||
photoUrl: '',
|
|
||||||
currentPassword: '',
|
currentPassword: '',
|
||||||
newPassword: '',
|
newPassword: '',
|
||||||
confirmPassword: '',
|
confirmPassword: '',
|
||||||
});
|
});
|
||||||
|
const [profile, setProfile] = useState<UserProfile | null>(null);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (userProfile) {
|
fetchProfile();
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const fetchProfile = async () => {
|
||||||
|
try {
|
||||||
|
const response = await fetch('/api/user/profile');
|
||||||
|
if (response.ok) {
|
||||||
|
const data = await response.json();
|
||||||
|
setProfile(data);
|
||||||
setFormData({
|
setFormData({
|
||||||
name: userProfile.name || '',
|
name: data.name || '',
|
||||||
email: userProfile.email || '',
|
email: data.email || '',
|
||||||
photoUrl: userProfile.photoUrl || '',
|
|
||||||
currentPassword: '',
|
currentPassword: '',
|
||||||
newPassword: '',
|
newPassword: '',
|
||||||
confirmPassword: '',
|
confirmPassword: '',
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}, [userProfile]);
|
} catch (error) {
|
||||||
|
console.error('Erreur lors du chargement du profil:', error);
|
||||||
const handleInputChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
setAlertModal({
|
||||||
const { name, value } = e.target;
|
show: true,
|
||||||
setFormData((prev) => ({ ...prev, [name]: value }));
|
type: 'error',
|
||||||
};
|
title: 'Erreur',
|
||||||
|
message: 'Erreur lors du chargement du profil',
|
||||||
const handleImageUpload = (e: React.ChangeEvent<HTMLInputElement>) => {
|
});
|
||||||
const file = e.target.files?.[0];
|
} finally {
|
||||||
if (file) {
|
setLoadingProfile(false);
|
||||||
// Vérifier la taille du fichier (max 5MB)
|
|
||||||
if (file.size > 5 * 1024 * 1024) {
|
|
||||||
showNotification('L\'image est trop grande (max 5MB)', 'error');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Vérifier le type de fichier
|
|
||||||
if (!file.type.startsWith('image/')) {
|
|
||||||
showNotification('Veuillez sélectionner une image', 'error');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const reader = new FileReader();
|
|
||||||
reader.onloadend = () => {
|
|
||||||
setFormData((prev) => ({ ...prev, photoUrl: reader.result as string }));
|
|
||||||
};
|
|
||||||
reader.readAsDataURL(file);
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -79,28 +70,25 @@ export default function ModifierCompteContent() {
|
|||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
|
|
||||||
try {
|
|
||||||
// Vérifier que les mots de passe correspondent si un nouveau mot de passe est fourni
|
// Vérifier que les mots de passe correspondent si un nouveau mot de passe est fourni
|
||||||
if (formData.newPassword) {
|
if (formData.newPassword && formData.newPassword !== formData.confirmPassword) {
|
||||||
if (formData.newPassword !== formData.confirmPassword) {
|
setAlertModal({
|
||||||
showNotification('Les mots de passe ne correspondent pas', 'error');
|
show: true,
|
||||||
|
type: 'error',
|
||||||
|
title: 'Erreur',
|
||||||
|
message: 'Les mots de passe ne correspondent pas',
|
||||||
|
});
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (formData.newPassword.length < 6) {
|
try {
|
||||||
showNotification('Le mot de passe doit contenir au moins 6 caractères', 'error');
|
|
||||||
setLoading(false);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const updateData: any = {
|
const updateData: any = {
|
||||||
name: formData.name.trim() || null,
|
name: formData.name,
|
||||||
email: formData.email.trim(),
|
email: formData.email,
|
||||||
photoUrl: formData.photoUrl || null,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Ajouter le changement de mot de passe si fourni
|
||||||
if (formData.newPassword) {
|
if (formData.newPassword) {
|
||||||
updateData.currentPassword = formData.currentPassword;
|
updateData.currentPassword = formData.currentPassword;
|
||||||
updateData.newPassword = formData.newPassword;
|
updateData.newPassword = formData.newPassword;
|
||||||
@@ -114,238 +102,229 @@ export default function ModifierCompteContent() {
|
|||||||
body: JSON.stringify(updateData),
|
body: JSON.stringify(updateData),
|
||||||
});
|
});
|
||||||
|
|
||||||
const data = await response.json();
|
|
||||||
|
|
||||||
if (response.ok) {
|
if (response.ok) {
|
||||||
showNotification('Profil mis à jour avec succès', 'success');
|
setAlertModal({
|
||||||
mutate();
|
show: true,
|
||||||
|
type: 'success',
|
||||||
|
title: 'Succès',
|
||||||
|
message: 'Votre profil a été mis à jour avec succès',
|
||||||
|
});
|
||||||
// Réinitialiser les champs de mot de passe
|
// Réinitialiser les champs de mot de passe
|
||||||
setFormData((prev) => ({
|
setFormData({
|
||||||
...prev,
|
...formData,
|
||||||
currentPassword: '',
|
currentPassword: '',
|
||||||
newPassword: '',
|
newPassword: '',
|
||||||
confirmPassword: '',
|
confirmPassword: '',
|
||||||
}));
|
});
|
||||||
|
// Recharger le profil
|
||||||
|
fetchProfile();
|
||||||
} else {
|
} else {
|
||||||
showNotification(data.error || 'Erreur lors de la mise à jour', 'error');
|
const error = await response.json();
|
||||||
|
setAlertModal({
|
||||||
|
show: true,
|
||||||
|
type: 'error',
|
||||||
|
title: 'Erreur',
|
||||||
|
message: error.error || 'Une erreur est survenue',
|
||||||
|
});
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Erreur:', error);
|
console.error('Erreur:', error);
|
||||||
showNotification('Erreur lors de la mise à jour', 'error');
|
setAlertModal({
|
||||||
|
show: true,
|
||||||
|
type: 'error',
|
||||||
|
title: 'Erreur',
|
||||||
|
message: 'Une erreur est survenue',
|
||||||
|
});
|
||||||
} finally {
|
} finally {
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const getUserInitials = () => {
|
if (loadingProfile) {
|
||||||
if (userProfile?.name) {
|
|
||||||
const names = userProfile.name.split(' ');
|
|
||||||
if (names.length >= 2) {
|
|
||||||
return `${names[0].charAt(0)}${names[1].charAt(0)}`.toUpperCase();
|
|
||||||
}
|
|
||||||
return userProfile.name.charAt(0).toUpperCase();
|
|
||||||
}
|
|
||||||
return userProfile?.email?.charAt(0).toUpperCase() || 'U';
|
|
||||||
};
|
|
||||||
|
|
||||||
if (!userProfile) {
|
|
||||||
return (
|
return (
|
||||||
<div className="p-6">
|
<div className="p-6">
|
||||||
<div className="text-center py-12 text-gray-500">Chargement...</div>
|
<div className="text-center text-gray-500">Chargement...</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="p-6">
|
<div className="p-4 md:p-6">
|
||||||
<div className="mb-8">
|
<div className="mb-6">
|
||||||
<button
|
<h1 className="text-2xl md:text-3xl font-semibold text-cblack mb-1">
|
||||||
onClick={() => router.back()}
|
Modifier mon compte
|
||||||
className="flex items-center gap-2 text-gray-600 hover:text-gray-900 mb-4 transition-colors"
|
</h1>
|
||||||
>
|
<p className="text-sm text-cgray">
|
||||||
<svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
Gérez vos informations personnelles et votre mot de passe
|
||||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M15 19l-7-7 7-7" />
|
|
||||||
</svg>
|
|
||||||
Retour aux paramètres
|
|
||||||
</button>
|
|
||||||
<h1 className="text-3xl font-bold text-gray-900 mb-2">Modifier mon compte</h1>
|
|
||||||
<p className="text-sm text-gray-600">
|
|
||||||
Mettez à jour vos informations personnelles et votre mot de passe
|
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<form onSubmit={handleSubmit} className="max-w-3xl">
|
<div className="bg-white rounded-lg shadow-sm p-4 md:p-6">
|
||||||
<div className="bg-white rounded-xl shadow-sm border border-gray-200 p-8 space-y-8">
|
<form onSubmit={handleSubmit} className="space-y-4 md:space-y-6">
|
||||||
{/* Photo de profil */}
|
{/* Nom */}
|
||||||
<div>
|
<div>
|
||||||
<label className="block text-sm font-semibold text-gray-900 mb-4">
|
<label htmlFor="name" className="block text-sm font-medium text-gray-700 mb-1">
|
||||||
Photo de profil
|
Nom <span className="text-red-500">*</span>
|
||||||
</label>
|
</label>
|
||||||
<div className="flex items-center gap-6">
|
|
||||||
<div className="relative">
|
<div className="relative">
|
||||||
{formData.photoUrl ? (
|
<div className="absolute inset-y-0 left-0 pl-3 flex items-center pointer-events-none">
|
||||||
<img
|
<svg className="h-5 w-5 text-gray-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
src={formData.photoUrl}
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M16 7a4 4 0 11-8 0 4 4 0 018 0zM12 14a7 7 0 00-7 7h14a7 7 0 00-7-7z" />
|
||||||
alt="Photo de profil"
|
|
||||||
className="w-24 h-24 rounded-full object-cover border-4 border-gray-200"
|
|
||||||
/>
|
|
||||||
) : (
|
|
||||||
<div className="w-24 h-24 rounded-full bg-gradient-to-br from-lblue to-dblue flex items-center justify-center border-4 border-gray-200">
|
|
||||||
<span className="text-white text-2xl font-bold">{getUserInitials()}</span>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
<div className="flex-1">
|
|
||||||
<label className="block">
|
|
||||||
<input
|
|
||||||
type="file"
|
|
||||||
accept="image/*"
|
|
||||||
onChange={handleImageUpload}
|
|
||||||
className="hidden"
|
|
||||||
/>
|
|
||||||
<span className="inline-flex items-center gap-2 px-4 py-2 bg-gray-50 border border-gray-300 rounded-lg text-sm font-medium text-gray-700 hover:bg-gray-100 cursor-pointer transition-colors">
|
|
||||||
<svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
||||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M4 16l4.586-4.586a2 2 0 012.828 0L16 16m-2-2l1.586-1.586a2 2 0 012.828 0L20 14m-6-6h.01M6 20h12a2 2 0 002-2V6a2 2 0 00-2-2H6a2 2 0 00-2 2v12a2 2 0 002 2z" />
|
|
||||||
</svg>
|
</svg>
|
||||||
Choisir une image
|
|
||||||
</span>
|
|
||||||
</label>
|
|
||||||
{formData.photoUrl && (
|
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
onClick={() => setFormData((prev) => ({ ...prev, photoUrl: '' }))}
|
|
||||||
className="mt-2 text-sm text-red-600 hover:text-red-700"
|
|
||||||
>
|
|
||||||
Supprimer la photo
|
|
||||||
</button>
|
|
||||||
)}
|
|
||||||
<p className="text-xs text-gray-500 mt-2">JPG, PNG ou GIF (max 5MB)</p>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Nom et prénom */}
|
|
||||||
<div>
|
|
||||||
<label htmlFor="name" className="block text-sm font-semibold text-gray-900 mb-2">
|
|
||||||
Nom et prénom
|
|
||||||
</label>
|
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
id="name"
|
id="name"
|
||||||
name="name"
|
required
|
||||||
value={formData.name}
|
value={formData.name}
|
||||||
onChange={handleInputChange}
|
onChange={(e) => setFormData({ ...formData, name: e.target.value })}
|
||||||
className="w-full px-4 py-2.5 border border-gray-300 rounded-lg text-gray-900 focus:outline-none focus:ring-2 focus:ring-lblue focus:border-transparent"
|
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"
|
||||||
placeholder="Votre nom complet"
|
placeholder="Votre nom"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
{/* Email */}
|
{/* Email */}
|
||||||
<div>
|
<div>
|
||||||
<label htmlFor="email" className="block text-sm font-semibold text-gray-900 mb-2">
|
<label htmlFor="email" className="block text-sm font-medium text-gray-700 mb-1">
|
||||||
Email
|
Email <span className="text-red-500">*</span>
|
||||||
</label>
|
</label>
|
||||||
|
<div className="relative">
|
||||||
|
<div className="absolute inset-y-0 left-0 pl-3 flex items-center pointer-events-none">
|
||||||
|
<svg className="h-5 w-5 text-gray-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M3 8l7.89 5.26a2 2 0 002.22 0L21 8M5 19h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v10a2 2 0 002 2z" />
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
<input
|
<input
|
||||||
type="email"
|
type="email"
|
||||||
id="email"
|
id="email"
|
||||||
name="email"
|
|
||||||
value={formData.email}
|
|
||||||
onChange={handleInputChange}
|
|
||||||
required
|
required
|
||||||
className="w-full px-4 py-2.5 border border-gray-300 rounded-lg text-gray-900 focus:outline-none focus:ring-2 focus:ring-lblue focus:border-transparent"
|
value={formData.email}
|
||||||
|
onChange={(e) => 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"
|
||||||
placeholder="votre@email.com"
|
placeholder="votre@email.com"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
{/* Section changement de mot de passe */}
|
{/* Rôle (lecture seule) */}
|
||||||
<div className="pt-6 border-t border-gray-200">
|
{profile?.role && (
|
||||||
<h3 className="text-lg font-semibold text-gray-900 mb-4">Changer le mot de passe</h3>
|
<div>
|
||||||
<p className="text-sm text-gray-600 mb-6">
|
<label className="block text-sm font-medium text-gray-700 mb-1">
|
||||||
|
Rôle
|
||||||
|
</label>
|
||||||
|
<div className="px-3 py-2 bg-gray-50 border border-gray-300 rounded-lg text-gray-600">
|
||||||
|
{profile.role.name}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{/* Séparateur */}
|
||||||
|
<div className="border-t border-gray-200 pt-4 md:pt-6">
|
||||||
|
<h2 className="text-lg font-semibold text-gray-900 mb-4">
|
||||||
|
Changer le mot de passe
|
||||||
|
</h2>
|
||||||
|
<p className="text-sm text-gray-600 mb-4">
|
||||||
Laissez ces champs vides si vous ne souhaitez pas changer votre mot de passe
|
Laissez ces champs vides si vous ne souhaitez pas changer votre mot de passe
|
||||||
</p>
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div className="space-y-4">
|
{/* Mot de passe actuel */}
|
||||||
<div>
|
<div>
|
||||||
<label htmlFor="currentPassword" className="block text-sm font-semibold text-gray-900 mb-2">
|
<label htmlFor="currentPassword" className="block text-sm font-medium text-gray-700 mb-1">
|
||||||
Mot de passe actuel
|
Mot de passe actuel
|
||||||
</label>
|
</label>
|
||||||
|
<div className="relative">
|
||||||
|
<div className="absolute inset-y-0 left-0 pl-3 flex items-center pointer-events-none">
|
||||||
|
<svg className="h-5 w-5 text-gray-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M12 15v2m-6 4h12a2 2 0 002-2v-6a2 2 0 00-2-2H6a2 2 0 00-2 2v6a2 2 0 002 2zm10-10V7a4 4 0 00-8 0v4h8z" />
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
<input
|
<input
|
||||||
type="password"
|
type="password"
|
||||||
id="currentPassword"
|
id="currentPassword"
|
||||||
name="currentPassword"
|
|
||||||
value={formData.currentPassword}
|
value={formData.currentPassword}
|
||||||
onChange={handleInputChange}
|
onChange={(e) => setFormData({ ...formData, currentPassword: e.target.value })}
|
||||||
className="w-full px-4 py-2.5 border border-gray-300 rounded-lg text-gray-900 focus:outline-none focus:ring-2 focus:ring-lblue focus:border-transparent"
|
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"
|
||||||
placeholder="Entrez votre mot de passe actuel"
|
placeholder="Entrez votre mot de passe actuel"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Nouveau mot de passe */}
|
||||||
<div>
|
<div>
|
||||||
<label htmlFor="newPassword" className="block text-sm font-semibold text-gray-900 mb-2">
|
<label htmlFor="newPassword" className="block text-sm font-medium text-gray-700 mb-1">
|
||||||
Nouveau mot de passe
|
Nouveau mot de passe
|
||||||
</label>
|
</label>
|
||||||
|
<div className="relative">
|
||||||
|
<div className="absolute inset-y-0 left-0 pl-3 flex items-center pointer-events-none">
|
||||||
|
<svg className="h-5 w-5 text-gray-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M12 15v2m-6 4h12a2 2 0 002-2v-6a2 2 0 00-2-2H6a2 2 0 00-2 2v6a2 2 0 002 2zm10-10V7a4 4 0 00-8 0v4h8z" />
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
<input
|
<input
|
||||||
type="password"
|
type="password"
|
||||||
id="newPassword"
|
id="newPassword"
|
||||||
name="newPassword"
|
|
||||||
value={formData.newPassword}
|
value={formData.newPassword}
|
||||||
onChange={handleInputChange}
|
onChange={(e) => setFormData({ ...formData, newPassword: e.target.value })}
|
||||||
className="w-full px-4 py-2.5 border border-gray-300 rounded-lg text-gray-900 focus:outline-none focus:ring-2 focus:ring-lblue focus:border-transparent"
|
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"
|
||||||
placeholder="Au moins 6 caractères"
|
placeholder="Entrez votre nouveau mot de passe"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Confirmer le mot de passe */}
|
||||||
<div>
|
<div>
|
||||||
<label htmlFor="confirmPassword" className="block text-sm font-semibold text-gray-900 mb-2">
|
<label htmlFor="confirmPassword" className="block text-sm font-medium text-gray-700 mb-1">
|
||||||
Confirmer le nouveau mot de passe
|
Confirmer le nouveau mot de passe
|
||||||
</label>
|
</label>
|
||||||
|
<div className="relative">
|
||||||
|
<div className="absolute inset-y-0 left-0 pl-3 flex items-center pointer-events-none">
|
||||||
|
<svg className="h-5 w-5 text-gray-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M12 15v2m-6 4h12a2 2 0 002-2v-6a2 2 0 00-2-2H6a2 2 0 00-2 2v6a2 2 0 002 2zm10-10V7a4 4 0 00-8 0v4h8z" />
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
<input
|
<input
|
||||||
type="password"
|
type="password"
|
||||||
id="confirmPassword"
|
id="confirmPassword"
|
||||||
name="confirmPassword"
|
|
||||||
value={formData.confirmPassword}
|
value={formData.confirmPassword}
|
||||||
onChange={handleInputChange}
|
onChange={(e) => setFormData({ ...formData, confirmPassword: e.target.value })}
|
||||||
className="w-full px-4 py-2.5 border border-gray-300 rounded-lg text-gray-900 focus:outline-none focus:ring-2 focus:ring-lblue focus:border-transparent"
|
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"
|
||||||
placeholder="Confirmez votre nouveau mot de passe"
|
placeholder="Confirmez votre nouveau mot de passe"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Boutons d'action */}
|
{/* Boutons */}
|
||||||
<div className="flex items-center justify-end gap-4 pt-6 border-t border-gray-200">
|
<div className="flex flex-col-reverse md:flex-row justify-end gap-3 pt-4 border-t border-gray-200">
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
onClick={() => router.back()}
|
onClick={() => router.back()}
|
||||||
className="px-6 py-2.5 text-sm font-medium text-gray-700 bg-white border border-gray-300 rounded-lg hover:bg-gray-50 transition-colors"
|
className="px-4 py-2 border border-gray-300 rounded-lg text-gray-700 hover:bg-gray-50 transition-colors w-full md:w-auto"
|
||||||
>
|
>
|
||||||
Annuler
|
Annuler
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
type="submit"
|
type="submit"
|
||||||
disabled={loading}
|
disabled={loading}
|
||||||
className="px-6 py-2.5 bg-lblue text-white text-sm font-semibold rounded-lg hover:bg-dblue transition-colors disabled:opacity-50 disabled:cursor-not-allowed flex items-center gap-2"
|
className="px-4 py-2 bg-lgreen text-white rounded-lg hover:bg-dgreen transition-colors disabled:opacity-50 disabled:cursor-not-allowed w-full md:w-auto"
|
||||||
>
|
>
|
||||||
{loading ? (
|
{loading ? 'Enregistrement...' : 'Enregistrer les modifications'}
|
||||||
<>
|
|
||||||
<svg className="animate-spin h-5 w-5" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24">
|
|
||||||
<circle className="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" strokeWidth="4"></circle>
|
|
||||||
<path className="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path>
|
|
||||||
</svg>
|
|
||||||
Enregistrement...
|
|
||||||
</>
|
|
||||||
) : (
|
|
||||||
<>
|
|
||||||
<svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
||||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M5 13l4 4L19 7" />
|
|
||||||
</svg>
|
|
||||||
Enregistrer les modifications
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{/* Modal d'alerte */}
|
||||||
|
{alertModal && (
|
||||||
|
<AlertModal
|
||||||
|
isOpen={alertModal.show}
|
||||||
|
type={alertModal.type}
|
||||||
|
title={alertModal.title}
|
||||||
|
message={alertModal.message}
|
||||||
|
onClose={() => setAlertModal(null)}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -113,16 +113,16 @@ export default function NewConversationModal({
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="fixed inset-0 z-50 flex items-center justify-center bg-black/50">
|
<div className="fixed inset-0 z-50 flex items-center justify-center bg-black/50 p-0 md:p-4">
|
||||||
<div className="bg-white rounded-lg shadow-xl w-full max-w-md mx-4">
|
<div className="bg-white rounded-none md:rounded-lg shadow-xl w-full h-full md:h-auto md:max-w-md md:mx-4 md:max-h-[90vh] flex flex-col">
|
||||||
<div className="p-6 border-b border-gray-200">
|
<div className="p-4 md:p-6 border-b border-gray-200 flex-shrink-0">
|
||||||
<div className="flex items-center justify-between">
|
<div className="flex items-center justify-between">
|
||||||
<h2 className="text-xl font-semibold text-gray-900">Nouvelle conversation</h2>
|
<h2 className="text-lg md:text-xl font-semibold text-gray-900">Nouvelle conversation</h2>
|
||||||
<button
|
<button
|
||||||
onClick={onClose}
|
onClick={onClose}
|
||||||
className="text-gray-400 hover:text-gray-600 transition-colors"
|
className="text-gray-400 hover:text-gray-600 transition-colors"
|
||||||
>
|
>
|
||||||
<svg className="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
<svg className="w-5 h-5 md:w-6 md:h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
<path
|
<path
|
||||||
strokeLinecap="round"
|
strokeLinecap="round"
|
||||||
strokeLinejoin="round"
|
strokeLinejoin="round"
|
||||||
@@ -134,7 +134,7 @@ export default function NewConversationModal({
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="p-6 space-y-4">
|
<div className="flex-1 overflow-y-auto p-4 md:p-6 space-y-4">
|
||||||
{/* Type de conversation */}
|
{/* Type de conversation */}
|
||||||
<div>
|
<div>
|
||||||
<label className="block text-sm font-medium text-gray-700 mb-2">
|
<label className="block text-sm font-medium text-gray-700 mb-2">
|
||||||
@@ -198,36 +198,36 @@ export default function NewConversationModal({
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Liste des utilisateurs */}
|
{/* Liste des utilisateurs */}
|
||||||
<div className="max-h-64 overflow-y-auto border border-gray-200 rounded-lg">
|
<div className="max-h-48 md:max-h-64 overflow-y-auto border border-gray-200 rounded-lg">
|
||||||
{error && (
|
{error && (
|
||||||
<div className="p-4 text-sm text-red-600">
|
<div className="p-3 md:p-4 text-sm text-red-600">
|
||||||
Erreur lors du chargement des utilisateurs
|
Erreur lors du chargement des utilisateurs
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
{!users && !error && (
|
{!users && !error && (
|
||||||
<div className="p-4 text-sm text-gray-500 text-center">Chargement...</div>
|
<div className="p-3 md:p-4 text-sm text-gray-500 text-center">Chargement...</div>
|
||||||
)}
|
)}
|
||||||
{availableUsers.length === 0 && users && (
|
{availableUsers.length === 0 && users && (
|
||||||
<div className="p-4 text-sm text-gray-500 text-center">Aucun utilisateur trouvé</div>
|
<div className="p-3 md:p-4 text-sm text-gray-500 text-center">Aucun utilisateur trouvé</div>
|
||||||
)}
|
)}
|
||||||
{availableUsers.length > 0 && (
|
{availableUsers.length > 0 && (
|
||||||
<div className="divide-y divide-gray-100">
|
<div className="divide-y divide-gray-100">
|
||||||
{availableUsers.map((user) => (
|
{availableUsers.map((user) => (
|
||||||
<label
|
<label
|
||||||
key={user.id}
|
key={user.id}
|
||||||
className="flex items-center gap-3 p-3 hover:bg-gray-50 cursor-pointer"
|
className="flex items-center gap-2 md:gap-3 p-2 md:p-3 hover:bg-gray-50 cursor-pointer"
|
||||||
>
|
>
|
||||||
<input
|
<input
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
checked={selectedUsers.includes(user.id)}
|
checked={selectedUsers.includes(user.id)}
|
||||||
onChange={() => handleUserToggle(user.id)}
|
onChange={() => handleUserToggle(user.id)}
|
||||||
className="rounded border-gray-300 text-lblue focus:ring-lblue"
|
className="rounded border-gray-300 text-lblue focus:ring-lblue w-4 h-4 md:w-auto md:h-auto"
|
||||||
/>
|
/>
|
||||||
<div className="flex-1">
|
<div className="flex-1 min-w-0">
|
||||||
<div className="text-sm font-medium text-gray-900">
|
<div className="text-sm font-medium text-gray-900 truncate">
|
||||||
{user.name || 'Utilisateur'}
|
{user.name || 'Utilisateur'}
|
||||||
</div>
|
</div>
|
||||||
<div className="text-xs text-gray-500">{user.email}</div>
|
<div className="text-xs text-gray-500 truncate">{user.email}</div>
|
||||||
</div>
|
</div>
|
||||||
</label>
|
</label>
|
||||||
))}
|
))}
|
||||||
@@ -245,17 +245,17 @@ export default function NewConversationModal({
|
|||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="p-6 border-t border-gray-200 flex justify-end gap-3">
|
<div className="p-4 md:p-6 border-t border-gray-200 flex flex-col-reverse md:flex-row justify-end gap-2 md:gap-3 flex-shrink-0">
|
||||||
<button
|
<button
|
||||||
onClick={onClose}
|
onClick={onClose}
|
||||||
className="px-4 py-2 text-gray-700 bg-gray-100 rounded-lg hover:bg-gray-200 transition-colors"
|
className="px-4 py-2 text-gray-700 bg-gray-100 rounded-lg hover:bg-gray-200 transition-colors w-full md:w-auto"
|
||||||
>
|
>
|
||||||
Annuler
|
Annuler
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
onClick={handleCreate}
|
onClick={handleCreate}
|
||||||
disabled={isCreating || selectedUsers.length === 0}
|
disabled={isCreating || selectedUsers.length === 0}
|
||||||
className="px-4 py-2 bg-lblue text-white rounded-lg hover:bg-dblue transition-colors disabled:opacity-50 disabled:cursor-not-allowed"
|
className="px-4 py-2 bg-lblue text-white rounded-lg hover:bg-dblue transition-colors disabled:opacity-50 disabled:cursor-not-allowed w-full md:w-auto"
|
||||||
>
|
>
|
||||||
{isCreating ? 'Création...' : 'Créer'}
|
{isCreating ? 'Création...' : 'Créer'}
|
||||||
</button>
|
</button>
|
||||||
|
|||||||
@@ -316,45 +316,45 @@ export default function ParametresContent() {
|
|||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="p-6">
|
<div className="p-4 md:p-6">
|
||||||
<div className="mb-8">
|
<div className="mb-6 md:mb-8">
|
||||||
<h1 className="text-3xl font-bold text-gray-900 mb-2">Paramètres</h1>
|
<h1 className="text-2xl md:text-3xl font-bold text-gray-900 mb-2">Paramètres</h1>
|
||||||
<p className="text-sm text-gray-600">
|
<p className="text-xs md:text-sm text-gray-600">
|
||||||
Gérez votre profil et configurez la plateforme
|
Gérez votre profil et configurez la plateforme
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="grid grid-cols-1 lg:grid-cols-2 gap-6">
|
<div className="grid grid-cols-1 lg:grid-cols-2 gap-4 md:gap-6">
|
||||||
{/* Colonne de gauche - Profil/Compte */}
|
{/* Colonne de gauche - Profil/Compte */}
|
||||||
<div>
|
<div>
|
||||||
<div className="bg-white rounded-xl shadow-sm border border-gray-100 p-6">
|
<div className="bg-white rounded-xl shadow-sm border border-gray-100 p-4 md:p-6">
|
||||||
<div className="text-center mb-6">
|
<div className="text-center mb-4 md:mb-6">
|
||||||
{user?.photoUrl ? (
|
{user?.photoUrl ? (
|
||||||
<img
|
<img
|
||||||
src={user.photoUrl}
|
src={user.photoUrl}
|
||||||
alt={user.name || 'Utilisateur'}
|
alt={user.name || 'Utilisateur'}
|
||||||
className="w-20 h-20 rounded-full object-cover mx-auto mb-4 shadow-lg border-4 border-white"
|
className="w-16 h-16 md:w-20 md:h-20 rounded-full object-cover mx-auto mb-3 md:mb-4 shadow-lg border-2 md:border-4 border-white"
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
<div className="w-20 h-20 rounded-full bg-gradient-to-br from-lblue to-dblue flex items-center justify-center mx-auto mb-4 shadow-lg">
|
<div className="w-16 h-16 md:w-20 md:h-20 rounded-full bg-gradient-to-br from-lblue to-dblue flex items-center justify-center mx-auto mb-3 md:mb-4 shadow-lg">
|
||||||
<span className="text-white text-2xl font-bold">{getUserInitials()}</span>
|
<span className="text-white text-xl md:text-2xl font-bold">{getUserInitials()}</span>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
<h2 className="text-xl font-bold text-gray-900 mb-1">
|
<h2 className="text-lg md:text-xl font-bold text-gray-900 mb-1">
|
||||||
{user?.name || 'Utilisateur'}
|
{user?.name || 'Utilisateur'}
|
||||||
</h2>
|
</h2>
|
||||||
<p className="text-sm text-gray-500">{user?.email}</p>
|
<p className="text-xs md:text-sm text-gray-500 break-words">{user?.email}</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<button
|
<button
|
||||||
onClick={() => router.push('/dashboard/parametres/compte')}
|
onClick={() => router.push('/dashboard/parametres/compte')}
|
||||||
className="w-full px-4 py-3 bg-gray-50 hover:bg-gray-100 rounded-lg text-left transition-colors border border-gray-200"
|
className="w-full px-3 md:px-4 py-2.5 md:py-3 bg-gray-50 hover:bg-gray-100 rounded-lg text-left transition-colors border border-gray-200"
|
||||||
>
|
>
|
||||||
<div className="flex items-center gap-3">
|
<div className="flex items-center gap-2 md:gap-3">
|
||||||
<svg className="w-5 h-5 text-gray-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
<svg className="w-4 h-4 md:w-5 md:h-5 text-gray-600 flex-shrink-0" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M16 7a4 4 0 11-8 0 4 4 0 018 0zM12 14a7 7 0 00-7 7h14a7 7 0 00-7-7z" />
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M16 7a4 4 0 11-8 0 4 4 0 018 0zM12 14a7 7 0 00-7 7h14a7 7 0 00-7-7z" />
|
||||||
</svg>
|
</svg>
|
||||||
<span className="font-medium text-gray-900">Modifier le compte</span>
|
<span className="font-medium text-sm md:text-base text-gray-900">Modifier le compte</span>
|
||||||
</div>
|
</div>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
@@ -365,24 +365,24 @@ export default function ParametresContent() {
|
|||||||
<div>
|
<div>
|
||||||
<button
|
<button
|
||||||
onClick={() => router.push('/dashboard/parametres/configuration')}
|
onClick={() => router.push('/dashboard/parametres/configuration')}
|
||||||
className="w-full bg-white rounded-xl shadow-sm border border-gray-100 p-6 hover:shadow-md hover:border-lblue transition-all text-left group"
|
className="w-full bg-white rounded-xl shadow-sm border border-gray-100 p-4 md:p-6 hover:shadow-md hover:border-lblue transition-all text-left group"
|
||||||
>
|
>
|
||||||
<div className="flex items-center gap-4">
|
<div className="flex items-center gap-3 md:gap-4">
|
||||||
<div className="w-16 h-16 rounded-xl bg-lblue/10 flex items-center justify-center group-hover:bg-lblue/20 transition-colors">
|
<div className="w-12 h-12 md:w-16 md:h-16 rounded-xl bg-lblue/10 flex items-center justify-center group-hover:bg-lblue/20 transition-colors flex-shrink-0">
|
||||||
<svg className="w-8 h-8 text-lblue" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
<svg className="w-6 h-6 md:w-8 md:h-8 text-lblue" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M10.325 4.317c.426-1.756 2.924-1.756 3.35 0a1.724 1.724 0 002.573 1.066c1.543-.94 3.31.826 2.37 2.37a1.724 1.724 0 001.065 2.572c1.756.426 1.756 2.924 0 3.35a1.724 1.724 0 00-1.066 2.573c.94 1.543-.826 3.31-2.37 2.37a1.724 1.724 0 00-2.572 1.065c-.426 1.756-2.924 1.756-3.35 0a1.724 1.724 0 00-2.573-1.066c-1.543.94-3.31-.826-2.37-2.37a1.724 1.724 0 00-1.065-2.572c-1.756-.426-1.756-2.924 0-3.35a1.724 1.724 0 001.066-2.573c-.94-1.543.826-3.31 2.37-2.37.996.608 2.296.07 2.572-1.065z" />
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M10.325 4.317c.426-1.756 2.924-1.756 3.35 0a1.724 1.724 0 002.573 1.066c1.543-.94 3.31.826 2.37 2.37a1.724 1.724 0 001.065 2.572c1.756.426 1.756 2.924 0 3.35a1.724 1.724 0 00-1.066 2.573c.94 1.543-.826 3.31-2.37 2.37a1.724 1.724 0 00-2.572 1.065c-.426 1.756-2.924 1.756-3.35 0a1.724 1.724 0 00-2.573-1.066c-1.543.94-3.31-.826-2.37-2.37a1.724 1.724 0 00-1.065-2.572c-1.756-.426-1.756-2.924 0-3.35a1.724 1.724 0 001.066-2.573c-.94-1.543.826-3.31 2.37-2.37.996.608 2.296.07 2.572-1.065z" />
|
||||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M15 12a3 3 0 11-6 0 3 3 0 016 0z" />
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M15 12a3 3 0 11-6 0 3 3 0 016 0z" />
|
||||||
</svg>
|
</svg>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex-1">
|
<div className="flex-1 min-w-0">
|
||||||
<h3 className="text-xl font-bold text-gray-900 mb-1 group-hover:text-lblue transition-colors">
|
<h3 className="text-lg md:text-xl font-bold text-gray-900 mb-1 group-hover:text-lblue transition-colors">
|
||||||
Configuration
|
Configuration
|
||||||
</h3>
|
</h3>
|
||||||
<p className="text-sm text-gray-600">
|
<p className="text-xs md:text-sm text-gray-600">
|
||||||
Configurez les paramètres de la plateforme
|
Configurez les paramètres de la plateforme
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<svg className="w-5 h-5 text-gray-400 group-hover:text-lblue transition-colors" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
<svg className="w-4 h-4 md:w-5 md:h-5 text-gray-400 group-hover:text-lblue transition-colors flex-shrink-0" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9 5l7 7-7 7" />
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9 5l7 7-7 7" />
|
||||||
</svg>
|
</svg>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ interface UniversProFormProps {
|
|||||||
|
|
||||||
export default function UniversProForm({ contact, onClose }: UniversProFormProps) {
|
export default function UniversProForm({ contact, onClose }: UniversProFormProps) {
|
||||||
const [loading, setLoading] = useState(false);
|
const [loading, setLoading] = useState(false);
|
||||||
|
const [isMobile, setIsMobile] = useState(false);
|
||||||
const [alertModal, setAlertModal] = useState<{
|
const [alertModal, setAlertModal] = useState<{
|
||||||
show: boolean;
|
show: boolean;
|
||||||
type: 'success' | 'error' | 'info' | 'warning';
|
type: 'success' | 'error' | 'info' | 'warning';
|
||||||
@@ -48,6 +49,17 @@ export default function UniversProForm({ contact, onClose }: UniversProFormProps
|
|||||||
}
|
}
|
||||||
}, [contact]);
|
}, [contact]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const checkMobile = () => {
|
||||||
|
setIsMobile(window.innerWidth < 768); // md breakpoint
|
||||||
|
};
|
||||||
|
|
||||||
|
checkMobile();
|
||||||
|
window.addEventListener('resize', checkMobile);
|
||||||
|
|
||||||
|
return () => window.removeEventListener('resize', checkMobile);
|
||||||
|
}, []);
|
||||||
|
|
||||||
const handleSubmit = async (e: React.FormEvent) => {
|
const handleSubmit = async (e: React.FormEvent) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
@@ -88,6 +100,51 @@ export default function UniversProForm({ contact, onClose }: UniversProFormProps
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Si on est sur mobile, afficher un message au lieu du formulaire
|
||||||
|
if (isMobile) {
|
||||||
|
return (
|
||||||
|
<div className="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50 p-4">
|
||||||
|
<div className="bg-white rounded-lg p-6 max-w-md w-full mx-4">
|
||||||
|
<div className="flex justify-between items-start mb-6">
|
||||||
|
<div className="flex-1">
|
||||||
|
<h2 className="text-xl font-semibold text-gray-900 mb-2">
|
||||||
|
{contact ? 'Modifier le contact' : 'Nouveau contact'}
|
||||||
|
</h2>
|
||||||
|
</div>
|
||||||
|
<button
|
||||||
|
onClick={onClose}
|
||||||
|
className="text-gray-400 hover:text-gray-600 ml-4"
|
||||||
|
>
|
||||||
|
<svg className="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M6 18L18 6M6 6l12 12" />
|
||||||
|
</svg>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="text-center py-8">
|
||||||
|
<div className="mb-6">
|
||||||
|
<svg className="w-16 h-16 mx-auto text-gray-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9.75 17L9 20l-1 1h8l-1-1-.75-3M3 13h18M5 17h14a2 2 0 002-2V5a2 2 0 00-2-2H5a2 2 0 00-2 2v10a2 2 0 002 2z" />
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
<h3 className="text-lg font-semibold text-gray-900 mb-3">
|
||||||
|
Utilisez un ordinateur
|
||||||
|
</h3>
|
||||||
|
<p className="text-sm text-gray-600 mb-6">
|
||||||
|
Pour {contact ? 'modifier' : 'créer'} un contact professionnel, veuillez utiliser un ordinateur. Cette fonctionnalité n'est pas optimisée pour les appareils mobiles.
|
||||||
|
</p>
|
||||||
|
<button
|
||||||
|
onClick={onClose}
|
||||||
|
className="px-6 py-2 bg-lblue text-white rounded-lg hover:bg-dblue transition-colors"
|
||||||
|
>
|
||||||
|
Fermer
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50">
|
<div className="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50">
|
||||||
<div className="bg-white rounded-lg p-6 max-w-2xl w-full mx-4 max-h-[90vh] overflow-y-auto">
|
<div className="bg-white rounded-lg p-6 max-w-2xl w-full mx-4 max-h-[90vh] overflow-y-auto">
|
||||||
|
|||||||
@@ -485,50 +485,54 @@ export default function UniversProTable() {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Boutons d'action */}
|
{/* Boutons d'action */}
|
||||||
<div className="flex gap-3">
|
<div className="flex gap-2 md:gap-3 w-full md:w-auto">
|
||||||
<button
|
<button
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setEditingContact(null);
|
setEditingContact(null);
|
||||||
setShowForm(true);
|
setShowForm(true);
|
||||||
}}
|
}}
|
||||||
className="flex items-center gap-2 px-4 py-2 bg-lgreen text-white rounded-lg hover:bg-dgreen transition-colors"
|
className="flex items-center gap-1 md:gap-2 px-3 md:px-4 py-2 bg-lgreen text-white rounded-lg hover:bg-dgreen transition-colors text-sm md:text-base flex-1 md:flex-none justify-center"
|
||||||
>
|
>
|
||||||
<svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
<svg className="w-4 h-4 md:w-5 md:h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M12 4v16m8-8H4" />
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M12 4v16m8-8H4" />
|
||||||
</svg>
|
</svg>
|
||||||
Nouveau contact
|
<span className="hidden sm:inline">Nouveau contact</span>
|
||||||
|
<span className="sm:hidden">Nouveau</span>
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
onClick={() => setShowImportModal(true)}
|
onClick={() => setShowImportModal(true)}
|
||||||
className="flex items-center gap-2 px-4 py-2 bg-lblue text-white rounded-lg hover:bg-dblue transition-colors"
|
className="flex items-center gap-1 md:gap-2 px-3 md:px-4 py-2 bg-lblue text-white rounded-lg hover:bg-dblue transition-colors text-sm md:text-base"
|
||||||
>
|
>
|
||||||
<svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
<svg className="w-4 h-4 md:w-5 md:h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M4 16v1a3 3 0 003 3h10a3 3 0 003-3v-1m-4-4l-4 4m0 0l-4-4m4 4V4" />
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M4 16v1a3 3 0 003 3h10a3 3 0 003-3v-1m-4-4l-4 4m0 0l-4-4m4 4V4" />
|
||||||
</svg>
|
</svg>
|
||||||
Importer
|
<span className="hidden sm:inline">Importer</span>
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
onClick={handleExport}
|
onClick={handleExport}
|
||||||
disabled={selectedIds.size === 0}
|
disabled={selectedIds.size === 0}
|
||||||
className="flex items-center gap-2 px-4 py-2 bg-lorange text-white rounded-lg hover:bg-dorange transition-colors disabled:opacity-50 disabled:cursor-not-allowed"
|
className="flex items-center gap-1 md:gap-2 px-3 md:px-4 py-2 bg-lorange text-white rounded-lg hover:bg-dorange transition-colors disabled:opacity-50 disabled:cursor-not-allowed text-sm md:text-base"
|
||||||
>
|
>
|
||||||
<svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
<svg className="w-4 h-4 md:w-5 md:h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M4 16v1a3 3 0 003 3h10a3 3 0 003-3v-1m-4-4l-4-4m0 0l-4-4m4 4V4" />
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M4 16v1a3 3 0 003 3h10a3 3 0 003-3v-1m-4-4l-4-4m0 0l-4-4m4 4V4" />
|
||||||
</svg>
|
</svg>
|
||||||
Exporter {selectedIds.size > 0 && `(${selectedIds.size})`}
|
<span className="hidden sm:inline">Exporter</span>
|
||||||
|
{selectedIds.size > 0 && <span className="ml-1">({selectedIds.size})</span>}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Tableau */}
|
{/* Tableau - Desktop */}
|
||||||
<div className="bg-white rounded-lg shadow-sm overflow-hidden">
|
<div className="bg-white rounded-lg shadow-sm overflow-hidden">
|
||||||
{loading ? (
|
{loading ? (
|
||||||
<div className="p-8 text-center text-gray-500">Chargement...</div>
|
<div className="p-8 text-center text-gray-500">Chargement...</div>
|
||||||
) : contacts.length === 0 ? (
|
) : contacts.length === 0 ? (
|
||||||
<div className="p-8 text-center text-gray-500">Aucun contact trouvé</div>
|
<div className="p-8 text-center text-gray-500">Aucun contact trouvé</div>
|
||||||
) : (
|
) : (
|
||||||
<div className="overflow-x-auto">
|
<>
|
||||||
|
{/* Vue desktop - Tableau */}
|
||||||
|
<div className="hidden md:block overflow-x-auto">
|
||||||
<table className="min-w-full divide-y divide-gray-200">
|
<table className="min-w-full divide-y divide-gray-200">
|
||||||
<thead className="bg-gray-50">
|
<thead className="bg-gray-50">
|
||||||
<tr>
|
<tr>
|
||||||
@@ -617,6 +621,98 @@ export default function UniversProTable() {
|
|||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{/* Vue mobile - Cartes */}
|
||||||
|
<div className="md:hidden divide-y divide-gray-200">
|
||||||
|
{contacts.map((contact) => (
|
||||||
|
<div key={contact.id} className="p-4 hover:bg-gray-50">
|
||||||
|
<div className="flex items-start gap-3">
|
||||||
|
{/* Checkbox */}
|
||||||
|
<input
|
||||||
|
type="checkbox"
|
||||||
|
checked={selectedIds.has(contact.id)}
|
||||||
|
onChange={(e) => handleSelectOne(contact.id, e.target.checked)}
|
||||||
|
className="w-4 h-4 text-lblue border-gray-300 rounded focus:ring-lblue mt-1"
|
||||||
|
/>
|
||||||
|
|
||||||
|
{/* Avatar */}
|
||||||
|
<div className="w-12 h-12 rounded-full bg-lblue flex items-center justify-center text-white font-semibold flex-shrink-0">
|
||||||
|
{getInitials(contact.nom, contact.prenom)}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Contenu principal */}
|
||||||
|
<div className="flex-1 min-w-0">
|
||||||
|
{/* Nom */}
|
||||||
|
<div className="mb-2">
|
||||||
|
<div className="text-base font-semibold text-gray-900">
|
||||||
|
{contact.prenom} {contact.nom}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Contact */}
|
||||||
|
<div className="mb-2">
|
||||||
|
<div className="text-xs font-medium text-gray-500 uppercase tracking-wide mb-1">Contact</div>
|
||||||
|
<a href={`tel:${contact.telephone}`} className="text-sm text-gray-900 block truncate">
|
||||||
|
{contact.telephone}
|
||||||
|
</a>
|
||||||
|
<a href={`mailto:${contact.email}`} className="text-sm text-gray-500 block truncate">
|
||||||
|
{contact.email}
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Adresse */}
|
||||||
|
<div className="mb-2">
|
||||||
|
<div className="text-xs font-medium text-gray-500 uppercase tracking-wide mb-1">Adresse</div>
|
||||||
|
<div className="text-sm text-gray-900 line-clamp-2">
|
||||||
|
{contact.adresse}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Entreprise */}
|
||||||
|
<div className="mb-3">
|
||||||
|
<div className="text-xs font-medium text-gray-500 uppercase tracking-wide mb-1">Entreprise</div>
|
||||||
|
<div className="text-sm font-medium text-gray-900">
|
||||||
|
{contact.nomEntreprise}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Actions */}
|
||||||
|
<div className="flex items-center gap-4 pt-2 border-t border-gray-200">
|
||||||
|
<button
|
||||||
|
onClick={() => handleView(contact.id)}
|
||||||
|
className="flex items-center gap-1 text-lblue hover:text-dblue text-sm"
|
||||||
|
>
|
||||||
|
<svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M15 12a3 3 0 11-6 0 3 3 0 016 0z" />
|
||||||
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M2.458 12C3.732 7.943 7.523 5 12 5c4.478 0 8.268 2.943 9.542 7-1.274 4.057-5.064 7-9.542 7-4.477 0-8.268-2.943-9.542-7z" />
|
||||||
|
</svg>
|
||||||
|
Voir
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
onClick={() => handleEdit(contact)}
|
||||||
|
className="flex items-center gap-1 text-lblue hover:text-dblue text-sm"
|
||||||
|
>
|
||||||
|
<svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M11 5H6a2 2 0 00-2 2v11a2 2 0 002 2h11a2 2 0 002-2v-5m-1.414-9.414a2 2 0 112.828 2.828L11.828 15H9v-2.828l8.586-8.586z" />
|
||||||
|
</svg>
|
||||||
|
Modifier
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
onClick={() => handleDelete(contact.id)}
|
||||||
|
className="flex items-center gap-1 text-red-500 hover:text-red-700 text-sm ml-auto"
|
||||||
|
>
|
||||||
|
<svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16" />
|
||||||
|
</svg>
|
||||||
|
Supprimer
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -864,29 +960,29 @@ export default function UniversProTable() {
|
|||||||
|
|
||||||
{/* Modal vue détaillée - Design épuré */}
|
{/* Modal vue détaillée - Design épuré */}
|
||||||
{viewingContact && (
|
{viewingContact && (
|
||||||
<div className="fixed inset-0 bg-black/60 backdrop-blur-sm flex items-center justify-center z-50 p-4 animate-fadeIn">
|
<div className="fixed inset-0 bg-black/60 backdrop-blur-sm flex items-center justify-center z-50 p-0 md:p-4 animate-fadeIn">
|
||||||
<div className="bg-white rounded-xl shadow-2xl max-w-5xl w-full max-h-[95vh] overflow-hidden flex flex-col animate-slideUp border border-gray-200">
|
<div className="bg-white rounded-none md:rounded-xl shadow-2xl max-w-5xl w-full h-full md:h-auto md:max-h-[95vh] overflow-hidden flex flex-col animate-slideUp border-0 md:border border-gray-200">
|
||||||
{/* Header épuré */}
|
{/* Header épuré */}
|
||||||
<div className="border-b border-gray-200 px-8 py-6 bg-white">
|
<div className="border-b border-gray-200 px-4 md:px-8 py-4 md:py-6 bg-white">
|
||||||
<div className="flex items-start justify-between">
|
<div className="flex items-start justify-between">
|
||||||
<div className="flex items-center gap-5">
|
<div className="flex items-center gap-3 md:gap-5 flex-1 min-w-0">
|
||||||
<div className="w-16 h-16 rounded-full bg-lblue flex items-center justify-center text-white font-bold text-xl border-4 border-lblue/10 shadow-sm">
|
<div className="w-12 h-12 md:w-16 md:h-16 rounded-full bg-lblue flex items-center justify-center text-white font-bold text-lg md:text-xl border-2 md:border-4 border-lblue/10 shadow-sm flex-shrink-0">
|
||||||
{getInitials(viewingContact.nom, viewingContact.prenom)}
|
{getInitials(viewingContact.nom, viewingContact.prenom)}
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div className="flex-1 min-w-0">
|
||||||
<h2 className="text-2xl font-bold text-gray-900 mb-1">
|
<h2 className="text-xl md:text-2xl font-bold text-gray-900 mb-1 truncate">
|
||||||
{viewingContact.prenom} {viewingContact.nom}
|
{viewingContact.prenom} {viewingContact.nom}
|
||||||
</h2>
|
</h2>
|
||||||
<p className="text-gray-500 text-sm">
|
<p className="text-gray-500 text-xs md:text-sm">
|
||||||
Informations détaillées du contact professionnel
|
Informations détaillées du contact professionnel
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<button
|
<button
|
||||||
onClick={() => setViewingContact(null)}
|
onClick={() => setViewingContact(null)}
|
||||||
className="text-gray-400 hover:text-gray-600 transition-colors p-2 hover:bg-gray-100 rounded-lg"
|
className="text-gray-400 hover:text-gray-600 transition-colors p-2 hover:bg-gray-100 rounded-lg flex-shrink-0 ml-2"
|
||||||
>
|
>
|
||||||
<svg className="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
<svg className="w-5 h-5 md:w-6 md:h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M6 18L18 6M6 6l12 12" />
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M6 18L18 6M6 6l12 12" />
|
||||||
</svg>
|
</svg>
|
||||||
</button>
|
</button>
|
||||||
@@ -895,60 +991,60 @@ export default function UniversProTable() {
|
|||||||
|
|
||||||
{/* Contenu scrollable */}
|
{/* Contenu scrollable */}
|
||||||
<div className="flex-1 overflow-y-auto">
|
<div className="flex-1 overflow-y-auto">
|
||||||
<div className="p-8">
|
<div className="p-4 md:p-8">
|
||||||
{/* Actions rapides */}
|
{/* Actions rapides */}
|
||||||
<div className="mb-8 flex flex-wrap gap-3">
|
<div className="mb-6 md:mb-8 flex flex-wrap gap-2 md:gap-3">
|
||||||
<a
|
<a
|
||||||
href={`tel:${viewingContact.telephone}`}
|
href={`tel:${viewingContact.telephone}`}
|
||||||
className="flex items-center gap-2 px-4 py-2 bg-gray-50 text-gray-700 border border-gray-200 rounded-lg hover:bg-gray-100 hover:border-lblue transition-colors"
|
className="flex items-center gap-2 px-3 md:px-4 py-2 bg-gray-50 text-gray-700 border border-gray-200 rounded-lg hover:bg-gray-100 hover:border-lblue transition-colors flex-1 md:flex-none justify-center"
|
||||||
>
|
>
|
||||||
<svg className="w-5 h-5 text-lblue" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
<svg className="w-4 h-4 md:w-5 md:h-5 text-lblue" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M3 5a2 2 0 012-2h3.28a1 1 0 01.948.684l1.498 4.493a1 1 0 01-.502 1.21l-2.257 1.13a11.042 11.042 0 005.516 5.516l1.13-2.257a1 1 0 011.21-.502l4.493 1.498a1 1 0 01.684.949V19a2 2 0 01-2 2h-1C9.716 21 3 14.284 3 6V5z" />
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M3 5a2 2 0 012-2h3.28a1 1 0 01.948.684l1.498 4.493a1 1 0 01-.502 1.21l-2.257 1.13a11.042 11.042 0 005.516 5.516l1.13-2.257a1 1 0 011.21-.502l4.493 1.498a1 1 0 01.684.949V19a2 2 0 01-2 2h-1C9.716 21 3 14.284 3 6V5z" />
|
||||||
</svg>
|
</svg>
|
||||||
<span className="text-sm font-medium">Appeler</span>
|
<span className="text-xs md:text-sm font-medium">Appeler</span>
|
||||||
</a>
|
</a>
|
||||||
<a
|
<a
|
||||||
href={`mailto:${viewingContact.email}`}
|
href={`mailto:${viewingContact.email}`}
|
||||||
className="flex items-center gap-2 px-4 py-2 bg-gray-50 text-gray-700 border border-gray-200 rounded-lg hover:bg-gray-100 hover:border-lblue transition-colors"
|
className="flex items-center gap-2 px-3 md:px-4 py-2 bg-gray-50 text-gray-700 border border-gray-200 rounded-lg hover:bg-gray-100 hover:border-lblue transition-colors flex-1 md:flex-none justify-center"
|
||||||
>
|
>
|
||||||
<svg className="w-5 h-5 text-lblue" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
<svg className="w-4 h-4 md:w-5 md:h-5 text-lblue" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M3 8l7.89 5.26a2 2 0 002.22 0L21 8M5 19h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v10a2 2 0 002 2z" />
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M3 8l7.89 5.26a2 2 0 002.22 0L21 8M5 19h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v10a2 2 0 002 2z" />
|
||||||
</svg>
|
</svg>
|
||||||
<span className="text-sm font-medium">Envoyer un email</span>
|
<span className="text-xs md:text-sm font-medium">Envoyer un email</span>
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="grid grid-cols-1 lg:grid-cols-2 gap-6">
|
<div className="grid grid-cols-1 lg:grid-cols-2 gap-4 md:gap-6">
|
||||||
{/* Carte Informations de contact */}
|
{/* Carte Informations de contact */}
|
||||||
<div className="bg-white rounded-xl border border-gray-200 p-6 shadow-sm">
|
<div className="bg-white rounded-lg md:rounded-xl border border-gray-200 p-4 md:p-6 shadow-sm">
|
||||||
<div className="flex items-center gap-3 mb-6 pb-4 border-b border-gray-200">
|
<div className="flex items-center gap-2 md:gap-3 mb-4 md:mb-6 pb-3 md:pb-4 border-b border-gray-200">
|
||||||
<div className="w-10 h-10 rounded-lg bg-lblue/10 flex items-center justify-center">
|
<div className="w-8 h-8 md:w-10 md:h-10 rounded-lg bg-lblue/10 flex items-center justify-center flex-shrink-0">
|
||||||
<svg className="w-5 h-5 text-lblue" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
<svg className="w-4 h-4 md:w-5 md:h-5 text-lblue" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M16 7a4 4 0 11-8 0 4 4 0 018 0zM12 14a7 7 0 00-7 7h14a7 7 0 00-7-7z" />
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M16 7a4 4 0 11-8 0 4 4 0 018 0zM12 14a7 7 0 00-7 7h14a7 7 0 00-7-7z" />
|
||||||
</svg>
|
</svg>
|
||||||
</div>
|
</div>
|
||||||
<h3 className="text-lg font-semibold text-gray-900">
|
<h3 className="text-base md:text-lg font-semibold text-gray-900">
|
||||||
Informations de contact
|
Informations de contact
|
||||||
</h3>
|
</h3>
|
||||||
</div>
|
</div>
|
||||||
<div className="space-y-4">
|
<div className="space-y-3 md:space-y-4">
|
||||||
<div className="flex items-start gap-4">
|
<div className="flex items-start gap-3 md:gap-4">
|
||||||
<div className="w-10 h-10 rounded-lg bg-gray-100 flex items-center justify-center flex-shrink-0">
|
<div className="w-8 h-8 md:w-10 md:h-10 rounded-lg bg-gray-100 flex items-center justify-center flex-shrink-0">
|
||||||
<svg className="w-5 h-5 text-gray-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
<svg className="w-4 h-4 md:w-5 md:h-5 text-gray-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M3 5a2 2 0 012-2h3.28a1 1 0 01.948.684l1.498 4.493a1 1 0 01-.502 1.21l-2.257 1.13a11.042 11.042 0 005.516 5.516l1.13-2.257a1 1 0 011.21-.502l4.493 1.498a1 1 0 01.684.949V19a2 2 0 01-2 2h-1C9.716 21 3 14.284 3 6V5z" />
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M3 5a2 2 0 012-2h3.28a1 1 0 01.948.684l1.498 4.493a1 1 0 01-.502 1.21l-2.257 1.13a11.042 11.042 0 005.516 5.516l1.13-2.257a1 1 0 011.21-.502l4.493 1.498a1 1 0 01.684.949V19a2 2 0 01-2 2h-1C9.716 21 3 14.284 3 6V5z" />
|
||||||
</svg>
|
</svg>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex-1 min-w-0">
|
<div className="flex-1 min-w-0">
|
||||||
<p className="text-xs font-medium text-gray-500 uppercase tracking-wide mb-1">Téléphone</p>
|
<p className="text-xs font-medium text-gray-500 uppercase tracking-wide mb-1">Téléphone</p>
|
||||||
<a href={`tel:${viewingContact.telephone}`} className="text-sm font-semibold text-lblue hover:text-dblue transition-colors">
|
<a href={`tel:${viewingContact.telephone}`} className="text-sm font-semibold text-lblue hover:text-dblue transition-colors break-all">
|
||||||
{viewingContact.telephone}
|
{viewingContact.telephone}
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="flex items-start gap-4">
|
<div className="flex items-start gap-3 md:gap-4">
|
||||||
<div className="w-10 h-10 rounded-lg bg-gray-100 flex items-center justify-center flex-shrink-0">
|
<div className="w-8 h-8 md:w-10 md:h-10 rounded-lg bg-gray-100 flex items-center justify-center flex-shrink-0">
|
||||||
<svg className="w-5 h-5 text-gray-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
<svg className="w-4 h-4 md:w-5 md:h-5 text-gray-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M3 8l7.89 5.26a2 2 0 002.22 0L21 8M5 19h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v10a2 2 0 002 2z" />
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M3 8l7.89 5.26a2 2 0 002.22 0L21 8M5 19h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v10a2 2 0 002 2z" />
|
||||||
</svg>
|
</svg>
|
||||||
</div>
|
</div>
|
||||||
@@ -960,43 +1056,43 @@ export default function UniversProTable() {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="flex items-start gap-4">
|
<div className="flex items-start gap-3 md:gap-4">
|
||||||
<div className="w-10 h-10 rounded-lg bg-gray-100 flex items-center justify-center flex-shrink-0">
|
<div className="w-8 h-8 md:w-10 md:h-10 rounded-lg bg-gray-100 flex items-center justify-center flex-shrink-0">
|
||||||
<svg className="w-5 h-5 text-gray-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
<svg className="w-4 h-4 md:w-5 md:h-5 text-gray-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M17.657 16.657L13.414 20.9a1.998 1.998 0 01-2.827 0l-4.244-4.243a8 8 0 1111.314 0z" />
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M17.657 16.657L13.414 20.9a1.998 1.998 0 01-2.827 0l-4.244-4.243a8 8 0 1111.314 0z" />
|
||||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M15 11a3 3 0 11-6 0 3 3 0 016 0z" />
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M15 11a3 3 0 11-6 0 3 3 0 016 0z" />
|
||||||
</svg>
|
</svg>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex-1 min-w-0">
|
<div className="flex-1 min-w-0">
|
||||||
<p className="text-xs font-medium text-gray-500 uppercase tracking-wide mb-1">Adresse</p>
|
<p className="text-xs font-medium text-gray-500 uppercase tracking-wide mb-1">Adresse</p>
|
||||||
<p className="text-sm font-semibold text-gray-900">{viewingContact.adresse}</p>
|
<p className="text-sm font-semibold text-gray-900 break-words">{viewingContact.adresse}</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Carte Informations entreprise */}
|
{/* Carte Informations entreprise */}
|
||||||
<div className="bg-white rounded-xl border border-gray-200 p-6 shadow-sm">
|
<div className="bg-white rounded-lg md:rounded-xl border border-gray-200 p-4 md:p-6 shadow-sm">
|
||||||
<div className="flex items-center gap-3 mb-6 pb-4 border-b border-gray-200">
|
<div className="flex items-center gap-2 md:gap-3 mb-4 md:mb-6 pb-3 md:pb-4 border-b border-gray-200">
|
||||||
<div className="w-10 h-10 rounded-lg bg-lblue/10 flex items-center justify-center">
|
<div className="w-8 h-8 md:w-10 md:h-10 rounded-lg bg-lblue/10 flex items-center justify-center flex-shrink-0">
|
||||||
<svg className="w-5 h-5 text-lblue" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
<svg className="w-4 h-4 md:w-5 md:h-5 text-lblue" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M19 21V5a2 2 0 00-2-2H7a2 2 0 00-2 2v16m14 0h2m-2 0h-5m-9 0H3m2 0h5M9 7h1m-1 4h1m4-4h1m-1 4h1m-5 10v-5a1 1 0 011-1h2a1 1 0 011 1v5m-4 0h4" />
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M19 21V5a2 2 0 00-2-2H7a2 2 0 00-2 2v16m14 0h2m-2 0h-5m-9 0H3m2 0h5M9 7h1m-1 4h1m4-4h1m-1 4h1m-5 10v-5a1 1 0 011-1h2a1 1 0 011 1v5m-4 0h4" />
|
||||||
</svg>
|
</svg>
|
||||||
</div>
|
</div>
|
||||||
<h3 className="text-lg font-semibold text-gray-900">
|
<h3 className="text-base md:text-lg font-semibold text-gray-900">
|
||||||
Informations entreprise
|
Informations entreprise
|
||||||
</h3>
|
</h3>
|
||||||
</div>
|
</div>
|
||||||
<div className="space-y-4">
|
<div className="space-y-3 md:space-y-4">
|
||||||
<div className="flex items-start gap-4">
|
<div className="flex items-start gap-3 md:gap-4">
|
||||||
<div className="w-10 h-10 rounded-lg bg-gray-100 flex items-center justify-center flex-shrink-0">
|
<div className="w-8 h-8 md:w-10 md:h-10 rounded-lg bg-gray-100 flex items-center justify-center flex-shrink-0">
|
||||||
<svg className="w-5 h-5 text-gray-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
<svg className="w-4 h-4 md:w-5 md:h-5 text-gray-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M19 21V5a2 2 0 00-2-2H7a2 2 0 00-2 2v16m14 0h2m-2 0h-5m-9 0H3m2 0h5M9 7h1m-1 4h1m4-4h1m-1 4h1m-5 10v-5a1 1 0 011-1h2a1 1 0 011 1v5m-4 0h4" />
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M19 21V5a2 2 0 00-2-2H7a2 2 0 00-2 2v16m14 0h2m-2 0h-5m-9 0H3m2 0h5M9 7h1m-1 4h1m4-4h1m-1 4h1m-5 10v-5a1 1 0 011-1h2a1 1 0 011 1v5m-4 0h4" />
|
||||||
</svg>
|
</svg>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex-1 min-w-0">
|
<div className="flex-1 min-w-0">
|
||||||
<p className="text-xs font-medium text-gray-500 uppercase tracking-wide mb-1">Nom de l'entreprise</p>
|
<p className="text-xs font-medium text-gray-500 uppercase tracking-wide mb-1">Nom de l'entreprise</p>
|
||||||
<p className="text-lg font-bold text-gray-900">{viewingContact.nomEntreprise}</p>
|
<p className="text-base md:text-lg font-bold text-gray-900">{viewingContact.nomEntreprise}</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -1006,11 +1102,11 @@ export default function UniversProTable() {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Footer avec actions */}
|
{/* Footer avec actions */}
|
||||||
<div className="border-t border-gray-200 px-8 py-5 bg-white">
|
<div className="border-t border-gray-200 px-4 md:px-8 py-4 md:py-5 bg-white">
|
||||||
<div className="flex items-center justify-between">
|
<div className="flex flex-col-reverse md:flex-row items-stretch md:items-center justify-between gap-3 md:gap-0">
|
||||||
<button
|
<button
|
||||||
onClick={() => setViewingContact(null)}
|
onClick={() => setViewingContact(null)}
|
||||||
className="px-5 py-2.5 text-sm font-medium text-gray-700 bg-white border border-gray-300 rounded-lg hover:bg-gray-50 transition-colors"
|
className="px-4 md:px-5 py-2.5 text-sm font-medium text-gray-700 bg-white border border-gray-300 rounded-lg hover:bg-gray-50 transition-colors w-full md:w-auto"
|
||||||
>
|
>
|
||||||
Fermer
|
Fermer
|
||||||
</button>
|
</button>
|
||||||
@@ -1019,9 +1115,9 @@ export default function UniversProTable() {
|
|||||||
setViewingContact(null);
|
setViewingContact(null);
|
||||||
handleEdit(viewingContact);
|
handleEdit(viewingContact);
|
||||||
}}
|
}}
|
||||||
className="px-6 py-2.5 bg-lblue text-white text-sm font-semibold rounded-lg hover:bg-dblue transition-colors flex items-center gap-2"
|
className="px-4 md:px-6 py-2.5 bg-lblue text-white text-sm font-semibold rounded-lg hover:bg-dblue transition-colors flex items-center justify-center gap-2 w-full md:w-auto"
|
||||||
>
|
>
|
||||||
<svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
<svg className="w-4 h-4 md:w-5 md:h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M11 5H6a2 2 0 00-2 2v11a2 2 0 002 2h11a2 2 0 002-2v-5m-1.414-9.414a2 2 0 112.828 2.828L11.828 15H9v-2.828l8.586-8.586z" />
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M11 5H6a2 2 0 00-2 2v11a2 2 0 002 2h11a2 2 0 002-2v-5m-1.414-9.414a2 2 0 112.828 2.828L11.828 15H9v-2.828l8.586-8.586z" />
|
||||||
</svg>
|
</svg>
|
||||||
Modifier le contact
|
Modifier le contact
|
||||||
|
|||||||
BIN
prisma/dev.db
BIN
prisma/dev.db
Binary file not shown.
Reference in New Issue
Block a user