import React, { useState, useEffect, useCallback, useMemo } from "react"; import { View, Text, StyleSheet, ScrollView, RefreshControl, TouchableOpacity, } from "react-native"; import { Ionicons } from "@expo/vector-icons"; import { spacing, fontSize } from "../../theme"; import { useTheme } from "../../context/ThemeContext"; import { getMyDeliveries, getMyStats } from "../../api/api_delivery"; import type { DeliveryItem, } from "../../api/types"; import type { StatPoint } from "../../api/api_delivery"; import LoadingSpinner from "../../components/ui/LoadingSpinner"; import Card from "../../components/ui/Card"; type Period = "day" | "week" | "month"; const BAR_MAX_HEIGHT = 110; const BAR_WIDTH = 36; const BAR_GAP = 8; function BarChart({ data, colors }: { data: StatPoint[]; colors: any }) { const maxVal = Math.max(...data.map((d) => d.count), 1); if (data.length === 0) { return ( Aucune donnée sur cette période ); } return ( {data.map((point, i) => { const val = point.count; const barH = Math.max(4, (val / maxVal) * BAR_MAX_HEIGHT); const isLast = i === data.length - 1; return ( {val > 0 ? String(val) : ""} 0 ? colors.accent : colors.border, borderRadius: 5, opacity: val > 0 ? 1 : 0.3, }} /> {point.label} ); })} ); } export default function StatsScreen() { const { colors } = useTheme(); const [deliveries, setDeliveries] = useState([]); const [byDay, setByDay] = useState([]); const [byWeek, setByWeek] = useState([]); const [byMonth, setByMonth] = useState([]); const [todayCount, setTodayCount] = useState(0); const [loading, setLoading] = useState(true); const [refreshing, setRefreshing] = useState(false); const [period, setPeriod] = useState("week"); const loadData = useCallback(async () => { try { const [delivRes, statsRes] = await Promise.all([ getMyDeliveries(), getMyStats(), ]); if (delivRes.success && delivRes.deliveries) setDeliveries(delivRes.deliveries); if (statsRes.success) { setByDay(statsRes.by_day ?? []); setByWeek(statsRes.by_week ?? []); setByMonth(statsRes.by_month ?? []); setTodayCount(statsRes.today_count ?? 0); } } catch { /* ignore */ } setLoading(false); }, []); useEffect(() => { loadData(); }, [loadData]); const onRefresh = async () => { setRefreshing(true); await loadData(); setRefreshing(false); }; const total = deliveries.length; const completed = deliveries.filter((d) => d.status === "livre" || d.status === "approved").length; const inProgress = deliveries.filter((d) => d.status === "en_route").length; const pending = deliveries.filter((d) => d.status === "assigned").length; const chartData = period === "day" ? byDay : period === "week" ? byWeek : byMonth; const periodTotal = useMemo( () => chartData.reduce((acc, p) => acc + p.count, 0), [chartData], ); const periodLabels: Record = { day: "30 derniers jours", week: "12 dernières semaines", month: "12 derniers mois", }; const styles = useMemo( () => StyleSheet.create({ container: { flex: 1, backgroundColor: colors.bgPrimary }, title: { color: colors.textWhite, fontSize: fontSize.xl, fontWeight: "700", marginBottom: spacing.l, }, grid: { flexDirection: "row", flexWrap: "wrap", gap: spacing.m }, statCard: { width: "47%", alignItems: "center", paddingVertical: spacing.l }, statValue: { fontSize: fontSize.xxl, fontWeight: "700", marginTop: spacing.s }, statLabel: { color: colors.textMuted, fontSize: fontSize.xs, marginTop: spacing.xs, textAlign: "center", }, sectionTitle: { color: colors.textWhite, fontSize: fontSize.lg, fontWeight: "700", marginBottom: spacing.m, marginTop: spacing.xl, }, periodRow: { flexDirection: "row", gap: spacing.s, marginBottom: spacing.m, }, periodBtn: { flex: 1, paddingVertical: spacing.s, borderRadius: 8, alignItems: "center", backgroundColor: colors.bgCard, borderWidth: 1, borderColor: colors.border, }, periodBtnActive: { backgroundColor: colors.accent + "22", borderColor: colors.accent, }, periodBtnText: { fontSize: fontSize.sm, fontWeight: "600", color: colors.textMuted, }, periodBtnTextActive: { color: colors.accent }, chartCard: { paddingBottom: spacing.s }, summaryRow: { flexDirection: "row", justifyContent: "space-between", marginTop: spacing.s, paddingTop: spacing.s, borderTopWidth: 1, borderTopColor: colors.border, }, summaryItem: { alignItems: "center", flex: 1 }, summaryValue: { color: colors.textWhite, fontSize: fontSize.lg, fontWeight: "700" }, summaryLabel: { color: colors.textMuted, fontSize: fontSize.xs, marginTop: 2 }, periodHint: { color: colors.textMuted, fontSize: fontSize.xs, marginBottom: spacing.s }, }), [colors], ); const summaryCards = [ { label: "Livraisons du jour", value: todayCount.toString(), icon: "today-outline" as const, color: colors.accent }, { label: "Total livraisons", value: total.toString(), icon: "cube-outline" as const, color: colors.accent }, { label: "Complétées", value: completed.toString(), icon: "checkmark-circle-outline" as const, color: colors.success }, { label: "En cours", value: inProgress.toString(), icon: "time-outline" as const, color: colors.warning }, { label: "En attente", value: pending.toString(), icon: "hourglass-outline" as const, color: colors.info }, ]; if (loading) return ; return ( } > Mes performances {/* Cartes résumé */} {summaryCards.map((s, i) => ( {s.value} {s.label} ))} {/* Section graphiques */} Évolution {/* Sélecteur période */} {(["day", "week", "month"] as Period[]).map((p) => ( setPeriod(p)} > {p === "day" ? "Jour" : p === "week" ? "Semaine" : "Mois"} ))} {periodLabels[period]} {chartData.length > 0 && ( {periodTotal} livraisons )} ); }