chore: add mobile app

This commit is contained in:
2026-02-07 22:33:02 +01:00
parent db34640acc
commit e89384ad4e
29327 changed files with 3886944 additions and 12 deletions
@@ -0,0 +1,140 @@
import React, { useState, useEffect, useCallback, useMemo } from "react";
import {
View,
Text,
StyleSheet,
ScrollView,
RefreshControl,
} from "react-native";
import { Ionicons } from "@expo/vector-icons";
import { spacing, fontSize } from "../../theme";
import { useTheme } from "../../context/ThemeContext";
import { getMyDeliveries, getMyStatus } from "../../api/api_delivery";
import type { DeliveryItem } from "../../api/types";
import LoadingSpinner from "../../components/ui/LoadingSpinner";
import Card from "../../components/ui/Card";
export default function StatsScreen() {
const { colors } = useTheme();
const [deliveries, setDeliveries] = useState<DeliveryItem[]>([]);
const [loading, setLoading] = useState(true);
const [refreshing, setRefreshing] = useState(false);
const loadData = useCallback(async () => {
try {
const res = await getMyDeliveries();
if (res.success && res.deliveries) setDeliveries(res.deliveries);
} 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").length;
const inProgress = deliveries.filter((d) => d.status === "en_route").length;
const pending = deliveries.filter((d) => d.status === "assigned").length;
const totalRevenue = deliveries
.filter((d) => d.status === "livre")
.reduce((s, d) => s + d.total_prix, 0);
const stats = [
{
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,
},
];
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",
},
}),
[colors],
);
if (loading) return <LoadingSpinner message="Chargement stats..." />;
return (
<ScrollView
style={styles.container}
contentContainerStyle={{ padding: spacing.l }}
refreshControl={
<RefreshControl
refreshing={refreshing}
onRefresh={onRefresh}
tintColor={colors.success}
/>
}
>
<Text style={styles.title}>Mes performances</Text>
<View style={styles.grid}>
{stats.map((s, i) => (
<Card key={i} style={styles.statCard}>
<Ionicons name={s.icon} size={28} color={s.color} />
<Text style={[styles.statValue, { color: s.color }]}>
{s.value}
</Text>
<Text style={styles.statLabel}>{s.label}</Text>
</Card>
))}
</View>
</ScrollView>
);
}