Added more Responsive to Mobile Device

This commit is contained in:
2026-02-16 19:03:56 +01:00
parent 1ec4c935c9
commit d2df24106c
10 changed files with 744 additions and 391 deletions

View File

@@ -1,13 +1,16 @@
'use client';
import { useState, useEffect } from 'react';
import { useState, useEffect, useRef } from 'react';
import { useRouter } from 'next/navigation';
import TrajetForm from './TrajetForm';
import AdherentForm from './AdherentForm';
import TrajetDetailModal from './TrajetDetailModal';
import { getParticipationRef } from '@/lib/participation-ref';
type PeriodType = 'day' | 'yesterday' | 'week' | 'month' | 'quarter' | 'year' | 'custom';
interface Stats {
periodLabel?: string;
participationsMois: {
montant: number;
nombreFactures: number;
@@ -53,23 +56,54 @@ interface DashboardContentProps {
userName?: string | null;
}
const PERIOD_OPTIONS: { value: PeriodType; label: string }[] = [
{ value: 'day', label: "Aujourd'hui" },
{ value: 'yesterday', label: 'Hier' },
{ value: 'week', label: 'Cette semaine' },
{ value: 'month', label: 'Ce mois-ci' },
{ value: 'quarter', label: 'Ce trimestre' },
{ value: 'year', label: 'Cette année' },
{ value: 'custom', label: 'Personnalisé' },
];
export default function DashboardContent({ userName }: DashboardContentProps) {
const router = useRouter();
const [stats, setStats] = useState<Stats | null>(null);
const [trajetsRecents, setTrajetsRecents] = useState<Trajet[]>([]);
const [loading, setLoading] = useState(true);
const [period, setPeriod] = useState<PeriodType>('month');
const [dropdownOpen, setDropdownOpen] = useState(false);
const dropdownRef = useRef<HTMLDivElement>(null);
const [customFrom, setCustomFrom] = useState('');
const [customTo, setCustomTo] = useState('');
const [showTrajetForm, setShowTrajetForm] = useState(false);
const [showAdherentForm, setShowAdherentForm] = useState(false);
const [selectedTrajet, setSelectedTrajet] = useState<Trajet | null>(null);
useEffect(() => {
fetchStats();
if (period === 'custom' && customFrom && customTo) {
fetchStatsCustom(customFrom, customTo);
} else if (period !== 'custom') {
fetchStats(period);
}
fetchTrajetsRecents();
}, [period, customFrom, customTo]);
useEffect(() => {
const handleClickOutside = (e: MouseEvent) => {
if (dropdownRef.current && !dropdownRef.current.contains(e.target as Node)) {
setDropdownOpen(false);
}
};
document.addEventListener('mousedown', handleClickOutside);
return () => document.removeEventListener('mousedown', handleClickOutside);
}, []);
const fetchStats = async () => {
const fetchStats = async (p: PeriodType = period) => {
if (p === 'custom') return;
try {
const response = await fetch('/api/dashboard/stats');
const params = new URLSearchParams({ period: p });
const response = await fetch(`/api/dashboard/stats?${params}`);
if (response.ok) {
const data = await response.json();
setStats(data);
@@ -79,6 +113,27 @@ export default function DashboardContent({ userName }: DashboardContentProps) {
}
};
const fetchStatsCustom = async (from: string, to: string) => {
try {
const params = new URLSearchParams({ from, to });
const response = await fetch(`/api/dashboard/stats?${params}`);
if (response.ok) {
const data = await response.json();
setStats(data);
}
} catch (error) {
console.error('Erreur lors du chargement des statistiques:', error);
}
};
const refreshStats = () => {
if (period === 'custom' && customFrom && customTo) {
fetchStatsCustom(customFrom, customTo);
} else if (period !== 'custom') {
fetchStats(period);
}
};
const fetchTrajetsRecents = async () => {
setLoading(true);
try {
@@ -112,46 +167,6 @@ export default function DashboardContent({ userName }: DashboardContentProps) {
return `${prenom.charAt(0)}${nom.charAt(0)}`.toUpperCase();
};
const getStatusColor = (statut: string) => {
switch (statut) {
case 'Validé':
return 'bg-green-100 text-green-700';
case 'Terminé':
return 'bg-green-100 text-green-700';
case 'En cours':
return 'bg-blue-100 text-blue-700';
case 'Planifié':
return 'bg-gray-100 text-gray-700';
case 'Annulé':
return 'bg-red-100 text-red-700';
default:
return 'bg-gray-100 text-gray-700';
}
};
const getStatusDotColor = (statut: string) => {
switch (statut) {
case 'Validé':
case 'Terminé':
return 'bg-green-500';
case 'En cours':
return 'bg-blue-500';
case 'Planifié':
return 'bg-yellow-500';
case 'Annulé':
return 'bg-red-500';
default:
return 'bg-gray-500';
}
};
const getChauffeurNumber = (chauffeur: { prenom: string; nom: string } | null | undefined) => {
if (!chauffeur) return null;
// Simple hash pour obtenir un numéro de chauffeur (1-5)
const hash = (chauffeur.prenom + chauffeur.nom).split('').reduce((acc, char) => acc + char.charCodeAt(0), 0);
return (hash % 5) + 1;
};
// Montant estimé par trajet (basé sur l'image)
const getMontantTrajet = () => {
return 6.80; // Montant moyen
@@ -159,14 +174,122 @@ export default function DashboardContent({ userName }: DashboardContentProps) {
return (
<div className="p-4 sm:p-6 space-y-6 sm:space-y-8">
{/* En-tête */}
{/* En-tête avec menu déroulant de période */}
<div className="mb-6 sm:mb-8">
<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>
</h1>
<p className="text-xs sm:text-sm text-gray-600">
Bienvenue sur votre tableau de bord.
</p>
<div className="flex flex-col sm:flex-row sm:items-start sm:justify-between gap-4 mb-2">
<div>
<h1 className="text-2xl sm:text-3xl font-bold text-gray-900">
Content de vous revoir <span className="text-dyellow">{userName || 'Utilisateur'}</span>
</h1>
<p className="text-xs sm:text-sm text-gray-600 mt-1">
Bienvenue sur votre tableau de bord.
</p>
</div>
<div className="relative flex-shrink-0" ref={dropdownRef}>
<button
type="button"
onClick={() => setDropdownOpen(!dropdownOpen)}
className="inline-flex items-center gap-2 px-4 py-2.5 text-sm font-medium text-gray-700 bg-white border border-gray-200/80 rounded-xl shadow-[0_1px_3px_rgba(0,0,0,0.06)] hover:shadow-[0_2px_8px_rgba(0,0,0,0.08)] hover:border-gray-200 transition-all duration-200 focus:outline-none focus:ring-2 focus:ring-dyellow/20 focus:border-dyellow"
>
<svg className="w-4 h-4 text-gray-500" 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" />
</svg>
<span>
{period === 'custom' && customFrom && customTo
? `${customFrom.split('-').reverse().slice(0, 2).join('/')}${customTo.split('-').reverse().slice(0, 2).join('/')}`
: PERIOD_OPTIONS.find((o) => o.value === period)?.label || 'Période'}
</span>
<svg className={`w-4 h-4 text-gray-400 transition-transform duration-200 ${dropdownOpen ? 'rotate-180' : ''}`} fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M19 9l-7 7-7-7" />
</svg>
</button>
{dropdownOpen && (
<div className="absolute right-0 top-full mt-2 w-64 bg-white rounded-xl shadow-[0_4px_20px_rgba(0,0,0,0.08)] border border-gray-100 py-2 z-50 overflow-hidden">
<div className="px-3 py-2 border-b border-gray-100">
<p className="text-xs font-semibold text-gray-400 uppercase tracking-wider">Période des statistiques</p>
</div>
<div className="max-h-72 overflow-y-auto">
{PERIOD_OPTIONS.filter((o) => o.value !== 'custom').map((opt) => (
<button
key={opt.value}
type="button"
onClick={() => {
setPeriod(opt.value);
setDropdownOpen(false);
}}
className={`w-full flex items-center gap-3 px-4 py-2.5 text-sm transition-colors ${
period === opt.value
? 'bg-dyellow/10 text-dyellow font-medium'
: 'text-gray-700 hover:bg-gray-50'
}`}
>
<span className="w-6 h-6 rounded-lg bg-gray-100 flex items-center justify-center flex-shrink-0">
<svg className="w-3.5 h-3.5 text-gray-500" 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" />
</svg>
</span>
{opt.label}
{period === opt.value && (
<svg className="w-4 h-4 ml-auto text-dyellow" fill="currentColor" viewBox="0 0 20 20">
<path fillRule="evenodd" d="M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z" clipRule="evenodd" />
</svg>
)}
</button>
))}
<div className="border-t border-gray-100 my-2" />
<button
type="button"
onClick={() => setPeriod('custom')}
className={`w-full flex items-center gap-3 px-4 py-2.5 text-sm transition-colors ${
period === 'custom'
? 'bg-dyellow/10 text-dyellow font-medium'
: 'text-gray-700 hover:bg-gray-50'
}`}
>
<span className="w-6 h-6 rounded-lg bg-gray-100 flex items-center justify-center flex-shrink-0">
<svg className="w-3.5 h-3.5 text-gray-500" 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" />
</svg>
</span>
Personnalisé
{period === 'custom' && (
<svg className="w-4 h-4 ml-auto text-dyellow" fill="currentColor" viewBox="0 0 20 20">
<path fillRule="evenodd" d="M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z" clipRule="evenodd" />
</svg>
)}
</button>
{period === 'custom' && (
<div className="px-4 py-3 bg-gray-50/80 border-t border-gray-100 space-y-3">
<p className="text-xs font-medium text-gray-600">Sélectionner une plage</p>
<div className="flex items-center gap-2">
<input
type="date"
value={customFrom}
onChange={(e) => setCustomFrom(e.target.value)}
className="flex-1 px-3 py-2 text-sm border border-gray-200 rounded-lg bg-white focus:ring-2 focus:ring-dyellow/30 focus:border-dyellow transition-shadow"
/>
<span className="text-gray-400 text-sm"></span>
<input
type="date"
value={customTo}
onChange={(e) => setCustomTo(e.target.value)}
className="flex-1 px-3 py-2 text-sm border border-gray-200 rounded-lg bg-white focus:ring-2 focus:ring-dyellow/30 focus:border-dyellow transition-shadow"
/>
</div>
<button
type="button"
onClick={() => setDropdownOpen(false)}
className="w-full py-2.5 text-sm font-medium text-white bg-dyellow hover:bg-dyellow/90 rounded-lg transition-colors"
>
Appliquer
</button>
</div>
)}
</div>
</div>
)}
</div>
</div>
</div>
{/* Statistiques */}
@@ -181,7 +304,9 @@ export default function DashboardContent({ userName }: DashboardContentProps) {
</div>
</div>
<div>
<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-[10px] sm:text-xs text-gray-600 mb-1.5 sm:mb-2 font-medium uppercase tracking-wide">
Participations {period === 'month' ? 'ce mois-ci' : 'sur la période'}
</p>
<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€'}
</p>
@@ -201,13 +326,15 @@ export default function DashboardContent({ userName }: DashboardContentProps) {
</div>
</div>
<div>
<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-[10px] sm:text-xs text-gray-600 mb-1.5 sm:mb-2 font-medium uppercase tracking-wide">
Trajets {period === 'day' ? "aujourd'hui" : period === 'yesterday' ? 'hier' : 'sur la période'}
</p>
<p className="text-2xl sm:text-3xl font-bold text-gray-900 mb-1.5 sm:mb-2">
{stats ? stats.trajetsAujourdhui.nombre : 0}
</p>
<p className="text-[10px] sm:text-xs text-gray-500 font-medium">
{stats && stats.trajetsAujourdhui.difference !== 0
? `${stats.trajetsAujourdhui.difference > 0 ? '+' : ''}${stats.trajetsAujourdhui.difference} vs hier`
? `${stats.trajetsAujourdhui.difference > 0 ? '+' : ''}${stats.trajetsAujourdhui.difference} vs période précédente`
: 'Aucun changement'}
</p>
</div>
@@ -223,13 +350,15 @@ export default function DashboardContent({ userName }: DashboardContentProps) {
</div>
</div>
<div>
<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-[10px] sm:text-xs text-gray-600 mb-1.5 sm:mb-2 font-medium uppercase tracking-wide">
Trajets réalisés {period === 'month' ? 'ce mois-ci' : 'sur la période'}
</p>
<p className="text-2xl sm:text-3xl font-bold text-gray-900 mb-1.5 sm:mb-2">
{stats ? stats.trajetsRealisesMois.nombre : 0}
</p>
<p className="text-[10px] sm:text-xs text-gray-500 font-medium">
{stats && stats.trajetsRealisesMois.pourcentageEvolution !== 0
? `${stats.trajetsRealisesMois.pourcentageEvolution > 0 ? '+' : ''}${stats.trajetsRealisesMois.pourcentageEvolution}% vs mois dernier`
? `${stats.trajetsRealisesMois.pourcentageEvolution > 0 ? '+' : ''}${stats.trajetsRealisesMois.pourcentageEvolution}% vs période précédente`
: 'Aucun changement'}
</p>
</div>
@@ -261,7 +390,14 @@ export default function DashboardContent({ userName }: DashboardContentProps) {
{/* Actions Rapides */}
<div>
<div className="flex items-center justify-between mb-3 sm:mb-4">
<h2 className="text-lg sm:text-xl font-bold text-gray-900">Actions Rapides</h2>
<h2 className="text-lg sm:text-xl font-bold text-gray-900 flex items-center gap-2">
<span className="w-8 h-8 rounded-lg bg-lblue/10 flex items-center justify-center">
<svg className="w-4 h-4 text-lblue" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M13 10V3L4 14h7v7l9-11h-7z" />
</svg>
</span>
Actions Rapides
</h2>
</div>
<div className="grid grid-cols-2 gap-3 sm:gap-4">
<button
@@ -323,10 +459,17 @@ export default function DashboardContent({ userName }: DashboardContentProps) {
{/* Trajets Récents */}
<div>
<div className="flex items-center justify-between mb-3 sm:mb-4">
<h2 className="text-lg sm:text-xl font-bold text-gray-900">Trajets Récents</h2>
<h2 className="text-lg sm:text-xl font-bold text-gray-900 tracking-tight flex items-center gap-2">
<span className="w-8 h-8 rounded-lg bg-lblue/10 flex items-center justify-center">
<svg className="w-4 h-4 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" />
</svg>
</span>
Trajets Récents
</h2>
<button
onClick={fetchTrajetsRecents}
className="text-xs sm:text-sm text-lblue hover:text-dblue font-medium flex items-center gap-1 sm:gap-2"
className="text-xs sm:text-sm text-gray-500 hover:text-lblue font-medium flex items-center gap-1.5 transition-colors"
>
<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" />
@@ -334,7 +477,7 @@ export default function DashboardContent({ userName }: DashboardContentProps) {
<span className="hidden sm:inline">Actualiser</span>
</button>
</div>
<div className="bg-white rounded-xl shadow-sm border border-gray-100 overflow-hidden">
<div className="bg-white rounded-xl shadow-[0_1px_3px_rgba(0,0,0,0.05)] border border-gray-100/80 overflow-hidden">
{loading ? (
<div className="text-center py-6 sm:py-8 text-sm text-gray-500">Chargement...</div>
) : trajetsRecents.length === 0 ? (
@@ -342,111 +485,74 @@ export default function DashboardContent({ userName }: DashboardContentProps) {
Aucun trajet créé récemment
</div>
) : (
<div className="p-4 sm:p-6 space-y-3">
{trajetsRecents.map((trajet) => (
<div
key={trajet.id}
onClick={() => setSelectedTrajet(trajet)}
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-3 sm:gap-4">
{/* Avatar adhérent */}
<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)}
</div>
{/* Informations principales */}
<div className="flex-1 min-w-0">
<div className="flex items-start justify-between gap-2 sm:gap-4 mb-2">
<div className="flex-1 min-w-0">
<h3 className="text-xs sm:text-sm font-semibold text-gray-900 truncate">
{trajet.adherent.prenom} {trajet.adherent.nom}
</h3>
<div className="flex flex-wrap items-center gap-2 sm:gap-3 mt-1">
{trajet.participations?.[0] && (
<span className="px-1.5 py-0.5 text-[10px] sm:text-xs font-mono font-medium rounded bg-lblue/10 text-lblue" title="Référence de prescription">
{getParticipationRef(trajet.participations[0].id)}
</span>
)}
<span className="text-[10px] sm:text-xs text-gray-500 flex items-center gap-1">
<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" />
</svg>
{formatDate(trajet.date)}
</span>
<span className="text-[10px] sm:text-xs text-gray-500 flex items-center gap-1">
<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" />
</svg>
{formatTime(trajet.date)}
</span>
<span
className={`px-1.5 sm:px-2 py-0.5 text-[10px] sm:text-xs font-medium rounded ${
trajet.statut === 'Terminé'
? 'bg-green-100 text-green-700'
: trajet.statut === 'En cours'
? 'bg-blue-100 text-blue-700'
: trajet.statut === 'Annulé'
? 'bg-red-100 text-red-700'
: trajet.statut === 'Validé'
? 'bg-green-100 text-green-700'
: 'bg-gray-100 text-gray-700'
}`}
>
{trajet.statut}
<div
className="overflow-x-auto"
style={{ WebkitOverflowScrolling: 'touch' } as React.CSSProperties}
>
<table className="w-full text-sm min-w-[420px]">
<thead>
<tr className="border-b border-gray-200 bg-gray-50/80">
<th className="text-left py-3 px-4 font-semibold text-gray-600">Adhérent</th>
<th className="text-left py-3 px-4 font-semibold text-gray-600">Statut</th>
<th className="text-left py-3 px-4 font-semibold text-gray-600">Fichier</th>
<th className="w-10 px-4"></th>
</tr>
</thead>
<tbody>
{trajetsRecents.map((trajet) => (
<tr
key={trajet.id}
onClick={() => setSelectedTrajet(trajet)}
className="border-b border-gray-100 last:border-b-0 hover:bg-gray-50/80 transition-colors cursor-pointer"
>
<td className="py-3 px-4">
<div className="flex items-center gap-3">
<div className="w-9 h-9 rounded-full bg-lgreen flex items-center justify-center text-white text-xs font-semibold flex-shrink-0 shadow-sm">
{getInitials(trajet.adherent.nom, trajet.adherent.prenom)}
</div>
<div>
<span className="font-semibold text-gray-900">
{trajet.adherent.prenom} {trajet.adherent.nom}
</span>
<p className="text-xs text-gray-400 mt-0.5">
{formatDate(trajet.date)} · {formatTime(trajet.date)}
</p>
</div>
</div>
</div>
{/* Adresses */}
<div className="space-y-1 mb-2">
<div className="text-xs sm:text-sm text-gray-600">
<span className="font-medium">Départ:</span>{' '}
<span className="text-gray-900 break-words">{trajet.adresseDepart}</span>
</div>
<div className="text-xs sm:text-sm text-gray-600">
<span className="font-medium">Arrivée:</span>{' '}
<span className="text-gray-900 break-words">{trajet.adresseArrivee}</span>
</div>
</div>
{/* Chauffeur */}
<div className="flex items-center gap-2 sm:gap-4 mt-2 sm:mt-3">
{trajet.chauffeur ? (
<div className="flex items-center gap-2">
<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)}
</div>
<div>
<div className="text-[10px] sm:text-xs text-gray-500">Chauffeur</div>
<div className="text-xs sm:text-sm font-medium text-gray-900">
{trajet.chauffeur.prenom} {trajet.chauffeur.nom}
</div>
</div>
</div>
</td>
<td className="py-3 px-4">
<span
className={`inline-flex px-2 py-0.5 text-[11px] font-medium rounded-md ${
trajet.statut === 'Terminé' || trajet.statut === 'Validé'
? 'bg-emerald-50 text-emerald-700'
: trajet.statut === 'En cours'
? 'bg-sky-50 text-sky-700'
: trajet.statut === 'Annulé'
? 'bg-red-50 text-red-700'
: 'bg-gray-100 text-gray-600'
}`}
>
{trajet.statut}
</span>
</td>
<td className="py-3 px-4">
{trajet.participations?.[0] ? (
<span className="px-2 py-0.5 text-[11px] font-mono font-semibold rounded-md bg-lblue/15 text-lblue border border-lblue/20">
{getParticipationRef(trajet.participations[0].id)}
</span>
) : (
<div className="flex items-center gap-1.5 sm:gap-2">
<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" />
</svg>
<span className="text-xs sm:text-sm text-orange-600 font-medium">
Aucun chauffeur assigné
</span>
</div>
<span className="text-gray-300"></span>
)}
</div>
{/* Commentaire */}
{trajet.commentaire && (
<div className="mt-2 pt-2 border-t border-gray-200">
<p className="text-[10px] sm:text-xs text-gray-500 italic break-words">{trajet.commentaire}</p>
</div>
)}
</div>
</div>
</div>
))}
</td>
<td className="py-3 px-4">
<svg className="w-4 h-4 text-gray-300" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9 5l7 7-7 7" />
</svg>
</td>
</tr>
))}
</tbody>
</table>
</div>
)}
</div>
@@ -460,7 +566,7 @@ export default function DashboardContent({ userName }: DashboardContentProps) {
onSuccess={() => {
setShowTrajetForm(false);
fetchTrajetsRecents();
fetchStats();
refreshStats();
}}
/>
)}
@@ -470,7 +576,7 @@ export default function DashboardContent({ userName }: DashboardContentProps) {
adherent={null}
onClose={() => {
setShowAdherentForm(false);
fetchStats();
refreshStats();
}}
/>
)}
@@ -481,7 +587,7 @@ export default function DashboardContent({ userName }: DashboardContentProps) {
onClose={() => setSelectedTrajet(null)}
onUpdate={() => {
fetchTrajetsRecents();
fetchStats();
refreshStats();
}}
/>
)}