diff --git a/frontend-admin/src/api/api_admin.ts b/frontend-admin/src/api/api_admin.ts index d43a68d5..bf078f78 100644 --- a/frontend-admin/src/api/api_admin.ts +++ b/frontend-admin/src/api/api_admin.ts @@ -156,6 +156,15 @@ export const getAdminStats = async (): Promise => { return data; }; +export const getAdminDailyDetail = async ( + date: string, +): Promise => { + const { data } = await apiClient.get(`${V2}/admin/protected/stats/daily`, { + params: { date }, + }); + return data; +}; + export const resetAdminStats = async ( section: StatSection, ): Promise<{ success: boolean; reset_at: string }> => { diff --git a/frontend-admin/src/screens/admin/StatsScreen.tsx b/frontend-admin/src/screens/admin/StatsScreen.tsx index 9cb98a95..dd31f3d9 100644 --- a/frontend-admin/src/screens/admin/StatsScreen.tsx +++ b/frontend-admin/src/screens/admin/StatsScreen.tsx @@ -6,6 +6,7 @@ import { StyleSheet, RefreshControl, TouchableOpacity, + Modal, } from "react-native"; import AlertModal from "../../components/ui/AlertModal"; import { Ionicons } from "@expo/vector-icons"; @@ -18,6 +19,7 @@ import { getAdminStats, resetAdminStats, getAdminStatsByMonth, + getAdminDailyDetail, } from "../../api/api_admin"; import type { AdminStats, @@ -380,46 +382,15 @@ function SectionResetBtn({ ); } -// ── Section détail du jour ──────────────────────────────────────────────────── -function DailyDetailSection({ daily }: { daily: DailyDetail }) { +// ── Contenu détaillé d'un jour (catégories → produits) ─────────────────────── +// Réutilisé à la fois par la section "Activité du jour" (aujourd'hui) et par +// la modal de détail d'un jour cliqué dans l'historique mensuel. +function DailyDetailContent({ daily }: { daily: DailyDetail }) { const { colors } = useTheme(); const hasData = daily.categories.length > 0; return ( - - {/* En-tête */} - - - - - Activité du jour - - - - {daily.date} - - - + <> {/* Mini-résumé */} - Aucune commande aujourd'hui + Aucune commande ce jour-là ) : ( daily.categories.map((cat: DailyCategoryDetail, ci: number) => { @@ -676,6 +647,50 @@ function DailyDetailSection({ daily }: { daily: DailyDetail }) { ); }) )} + + ); +} + +// ── Section détail du jour (aujourd'hui, dans le flux principal) ──────────── +function DailyDetailSection({ daily }: { daily: DailyDetail }) { + const { colors } = useTheme(); + + return ( + + {/* En-tête */} + + + + + Activité du jour + + + + {daily.date} + + + + ); } @@ -747,6 +762,159 @@ const ddStyles = StyleSheet.create({ barFill: { height: "100%", borderRadius: 3 }, }); +// ── Modal — détail d'un jour cliqué dans l'historique mensuel ─────────────── +function DayDetailModal({ + visible, + dateLabel, + onClose, +}: { + visible: boolean; + dateLabel: string | null; // "2026-06-05" + onClose: () => void; +}) { + const { colors } = useTheme(); + const [detail, setDetail] = useState(null); + const [loading, setLoading] = useState(false); + const [errored, setErrored] = useState(false); + + useEffect(() => { + if (!visible || !dateLabel) return; + setLoading(true); + setErrored(false); + getAdminDailyDetail(dateLabel) + .then((data) => setDetail(data)) + .catch(() => setErrored(true)) + .finally(() => setLoading(false)); + }, [visible, dateLabel]); + + const prettyDate = useMemo(() => { + if (!dateLabel) return ""; + const [y, m, d] = dateLabel.split("-").map(Number); + return new Date(y, m - 1, d).toLocaleDateString("fr-FR", { + weekday: "long", + day: "2-digit", + month: "long", + year: "numeric", + }); + }, [dateLabel]); + + return ( + + + + + + + + {prettyDate} + + + + + + + + + {loading ? ( + + Chargement... + + ) : errored ? ( + + Impossible de charger le détail de ce jour + + ) : detail ? ( + + ) : null} + + + + + ); +} + +const dayModalStyles = StyleSheet.create({ + overlay: { + flex: 1, + backgroundColor: "rgba(0,0,0,0.55)", + justifyContent: "center", + padding: spacing.l, + }, + sheet: { + borderRadius: borderRadius.md, + borderWidth: 1, + padding: spacing.l, + maxHeight: "85%", + }, + header: { + flexDirection: "row", + alignItems: "center", + marginBottom: spacing.m, + }, + title: { + fontSize: fontSize.md, + fontWeight: "700", + textTransform: "capitalize", + }, + closeBtn: { + padding: spacing.xs, + }, +}); + // ── Helpers de mois (clé "YYYY-MM") ─────────────────────────────────────────── function monthKey(date: Date): string { const y = date.getFullYear(); @@ -773,6 +941,7 @@ function MonthlyHistorySection() { const [monthly, setMonthly] = useState(null); const [loadingMonth, setLoadingMonth] = useState(false); const [expanded, setExpanded] = useState(false); + const [selectedDay, setSelectedDay] = useState(null); const currentMonthKey = monthKey(new Date()); const isCurrentMonth = monthKeyState === currentMonthKey; @@ -1087,7 +1256,7 @@ function MonthlyHistorySection() { - {/* Liste détaillée jour par jour */} + {/* Liste détaillée jour par jour — cliquable */} {[...days].reverse().map((d) => { const isBest = best && d.day === best.day; const pct = @@ -1097,9 +1266,15 @@ function MonthlyHistorySection() { d.count > 0 ? 3 : 0, ) : 0; + const hasData = d.count > 0; return ( - + hasData && setSelectedDay(d.day) + } + disabled={!hasData} + activeOpacity={0.6} style={{ flexDirection: "row", alignItems: "center", @@ -1111,7 +1286,15 @@ function MonthlyHistorySection() { style={{ width: 42, fontSize: fontSize.xs, - color: colors.textMuted, + color: hasData + ? colors.textPrimary + : colors.textMuted, + fontWeight: hasData + ? "600" + : "400", + textDecorationLine: hasData + ? "underline" + : "none", }} > {d.label} @@ -1179,7 +1362,14 @@ function MonthlyHistorySection() { color={CHART_AMBER} /> )} - + {hasData && ( + + )} + ); })} @@ -1220,6 +1410,12 @@ function MonthlyHistorySection() { )} )} + + setSelectedDay(null)} + /> ); }