chore: build
Frontend Admin - EAS Build / build (push) Canceled after 0s
Frontend Client - EAS Build / build (push) Canceled after 0s
Backend - Build & Lint / build (push) Failing after 25m18s
Frontend Web - Build & Lint / build (push) Failing after 9m58s

This commit is contained in:
Xor290
2026-08-06 12:06:05 +02:00
parent c034088bee
commit 22a8d5026c
174 changed files with 30315 additions and 16120 deletions
@@ -0,0 +1,152 @@
import React, { useState, useCallback } from "react";
import {
View,
Text,
StyleSheet,
ScrollView,
RefreshControl,
} from "react-native";
import { Ionicons } from "@expo/vector-icons";
import { useFocusEffect } from "@react-navigation/native";
import { useTheme } from "../../context/ThemeContext";
import { spacing, fontSize } from "../../theme";
import { getMyRatings } from "../../api/api_delivery";
import type { LivreurRating } from "../../api/api_delivery";
import LoadingSpinner from "../../components/ui/LoadingSpinner";
const STAR_COLOR = "#f59e0b";
const STAR_EMPTY = "#374151";
function Stars({ value }: { value: number }) {
return (
<View style={{ flexDirection: "row", gap: 2 }}>
{[1, 2, 3, 4, 5].map((i) => (
<Ionicons
key={i}
name={i <= value ? "star" : "star-outline"}
size={14}
color={i <= value ? STAR_COLOR : STAR_EMPTY}
/>
))}
</View>
);
}
function formatDate(iso: string): string {
try {
return new Date(iso).toLocaleDateString("fr-FR", {
day: "2-digit",
month: "2-digit",
year: "numeric",
});
} catch {
return "";
}
}
export default function RatingsScreen() {
const { colors } = useTheme();
const [ratings, setRatings] = useState<LivreurRating[]>([]);
const [average, setAverage] = useState(0);
const [loading, setLoading] = useState(true);
const [refreshing, setRefreshing] = useState(false);
const load = useCallback(async (silent = false) => {
if (!silent) setLoading(true);
const res = await getMyRatings();
if (res.success) {
setRatings(res.ratings);
setAverage(res.average);
}
setLoading(false);
setRefreshing(false);
}, []);
useFocusEffect(useCallback(() => { load(); }, [load]));
const onRefresh = () => { setRefreshing(true); load(true); };
const styles = StyleSheet.create({
container: { flex: 1, backgroundColor: colors.bgPrimary },
content: { padding: spacing.l, paddingBottom: spacing.xxxl },
headerCard: {
backgroundColor: colors.bgCard,
borderRadius: 14,
padding: spacing.l,
marginBottom: spacing.l,
alignItems: "center",
borderWidth: 1,
borderColor: colors.borderLight,
},
avgNumber: { fontSize: 48, fontWeight: "800", color: STAR_COLOR, lineHeight: 56 },
avgLabel: { fontSize: fontSize.sm, color: colors.textMuted, marginTop: spacing.xs },
countLabel: { fontSize: fontSize.xs, color: colors.textMuted, marginTop: 4 },
starsRow: { flexDirection: "row", gap: 4, marginTop: spacing.s },
card: {
backgroundColor: colors.bgCard,
borderRadius: 12,
padding: spacing.l,
marginBottom: spacing.m,
borderWidth: 1,
borderColor: colors.borderLight,
},
cardHeader: { flexDirection: "row", justifyContent: "space-between", alignItems: "center", marginBottom: spacing.s },
client: { fontSize: fontSize.sm, fontWeight: "600", color: colors.textPrimary },
date: { fontSize: fontSize.xs, color: colors.textMuted },
orderRef: { fontSize: fontSize.xs, color: colors.textMuted, marginBottom: spacing.s },
comment: { fontSize: fontSize.sm, color: colors.textSecondary, fontStyle: "italic", marginTop: spacing.s, lineHeight: 20 },
emptyWrap: { alignItems: "center", paddingVertical: spacing.xxxl },
emptyText: { color: colors.textMuted, fontSize: fontSize.md, marginTop: spacing.m, textAlign: "center" },
});
if (loading) return <LoadingSpinner message="Chargement des avis..." />;
return (
<ScrollView
style={styles.container}
contentContainerStyle={styles.content}
refreshControl={<RefreshControl refreshing={refreshing} onRefresh={onRefresh} tintColor={STAR_COLOR} />}
>
{/* Résumé */}
<View style={styles.headerCard}>
<Text style={styles.avgNumber}>
{average > 0 ? average.toFixed(1) : "—"}
</Text>
<View style={styles.starsRow}>
{[1, 2, 3, 4, 5].map((i) => (
<Ionicons
key={i}
name={i <= Math.round(average) ? "star" : "star-outline"}
size={22}
color={i <= Math.round(average) ? STAR_COLOR : STAR_EMPTY}
/>
))}
</View>
<Text style={styles.avgLabel}>Note moyenne</Text>
<Text style={styles.countLabel}>{ratings.length} avis client{ratings.length > 1 ? "s" : ""}</Text>
</View>
{/* Liste */}
{ratings.length === 0 ? (
<View style={styles.emptyWrap}>
<Ionicons name="chatbubble-ellipses-outline" size={48} color={colors.textMuted} />
<Text style={styles.emptyText}>Aucun avis reçu pour l'instant</Text>
</View>
) : (
ratings.map((r) => (
<View key={r.id} style={styles.card}>
<View style={styles.cardHeader}>
<Text style={styles.client}>{r.client_username}</Text>
<Text style={styles.date}>{formatDate(r.created_at)}</Text>
</View>
<Text style={styles.orderRef}>Commande #{r.order_id}</Text>
<Stars value={r.rating} />
{r.comment ? (
<Text style={styles.comment}>"{r.comment}"</Text>
) : null}
</View>
))
)}
</ScrollView>
);
}
@@ -89,6 +89,7 @@ export default function StatsScreen() {
const [byDay, setByDay] = useState<StatPoint[]>([]);
const [byWeek, setByWeek] = useState<StatPoint[]>([]);
const [byMonth, setByMonth] = useState<StatPoint[]>([]);
const [todayCount, setTodayCount] = useState(0);
const [loading, setLoading] = useState(true);
const [refreshing, setRefreshing] = useState(false);
const [period, setPeriod] = useState<Period>("week");
@@ -104,6 +105,7 @@ export default function StatsScreen() {
setByDay(statsRes.by_day ?? []);
setByWeek(statsRes.by_week ?? []);
setByMonth(statsRes.by_month ?? []);
setTodayCount(statsRes.today_count ?? 0);
}
} catch {
/* ignore */
@@ -205,6 +207,7 @@ export default function StatsScreen() {
);
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 },