import { useEffect, useMemo, useState } from 'react'; import { BarChart3, CalendarDays, CarFront, Clock3, RefreshCw, Sun } from 'lucide-react'; import MainCard from '@/components/MainCard'; import LoadingCard from '@/components/LoadingCard'; import { Button } from '@/components/ui/button'; import { Badge } from '@/components/ui/badge'; import { timeApi } from '@/api/time'; import { planningApi } from '@/api/planning'; import { useAuth } from '@/context/useAuth'; import { dateKey, formatMinutes, personLabel, inputClass } from '@/components/planning/utils'; import StatisticsPeriodSelector from '@/components/statistics/StatisticsPeriodSelector'; const StatCard = ({ icon: Icon, label, value, detail }) => (

{label}

{value}

{detail &&

{detail}

}
); const rangeLabel = (stats) => { if (!stats) return ''; const start = new Date(`${stats.start}T12:00:00`); const end = new Date(`${stats.end}T12:00:00`); if (stats.period === 'day') { return start.toLocaleDateString('fr-FR', { weekday: 'long', day: 'numeric', month: 'long', year: 'numeric' }); } if (stats.period === 'week') { const startText = start.toLocaleDateString('fr-FR', { day: 'numeric', month: 'short' }); const endText = end.toLocaleDateString('fr-FR', { day: 'numeric', month: 'long', year: 'numeric' }); return `Du ${startText} au ${endText}`; } if (stats.period === 'month') { return start.toLocaleDateString('fr-FR', { month: 'long', year: 'numeric' }); } return String(start.getFullYear()); }; export default function UserStatisticsPage() { const { user } = useAuth(); const [people, setPeople] = useState([]); const [selectedUserId, setSelectedUserId] = useState(''); const [period, setPeriod] = useState('month'); const [anchor, setAnchor] = useState(dateKey()); const [stats, setStats] = useState(null); const [loadingPeople, setLoadingPeople] = useState(true); const [loading, setLoading] = useState(false); const [error, setError] = useState(''); const [reload, setReload] = useState(0); const statsPermission = user?.management_permissions?.view_statistics; const allowedIds = useMemo(() => { if (user?.is_admin || statsPermission?.all_users) return null; return new Set((statsPermission?.user_ids || []).map(String)); }, [user, statsPermission]); useEffect(() => { const controller = new AbortController(); setLoadingPeople(true); setError(''); planningApi.people(controller.signal) .then((rows) => { const visible = (Array.isArray(rows) ? rows : []).filter((person) => ( user?.is_admin || allowedIds === null || allowedIds.has(String(person.id)) )); setPeople(visible); setSelectedUserId((current) => ( visible.some((person) => String(person.id) === String(current)) ? String(current) : String(visible[0]?.id || '') )); }) .catch((e) => { if (!controller.signal.aborted) setError(e.message); }) .finally(() => { if (!controller.signal.aborted) setLoadingPeople(false); }); return () => controller.abort(); }, [user, allowedIds]); useEffect(() => { if (!selectedUserId) { setStats(null); return undefined; } const controller = new AbortController(); setLoading(true); setError(''); timeApi.getStats({ user_id: selectedUserId, period, anchor }, controller.signal) .then((data) => { if (!controller.signal.aborted) setStats(data); }) .catch((e) => { if (!controller.signal.aborted) setError(e.message); }) .finally(() => { if (!controller.signal.aborted) setLoading(false); }); return () => controller.abort(); }, [selectedUserId, period, anchor, reload]); if (loadingPeople) { return ; } return (
{error &&

{error}

} {stats && ( <>

{rangeLabel(stats)}

{personLabel(stats.user)}

{stats.incomplete_entries > 0 && ( {stats.incomplete_entries} pointage(s) incomplet(s) non compté(s) )}
)}
); }