diff --git a/frontend-admin/src/api/api_admin.ts b/frontend-admin/src/api/api_admin.ts
index cbb2cc9b..6d352e5f 100644
--- a/frontend-admin/src/api/api_admin.ts
+++ b/frontend-admin/src/api/api_admin.ts
@@ -104,6 +104,28 @@ export interface ProductQuantityBreakdown {
quantities: QuantityStat[];
}
+export interface DailyProductItem {
+ product_id: number;
+ name: string;
+ quantity: number;
+ order_count: number;
+ revenue: number;
+}
+export interface DailyCategoryDetail {
+ category: string;
+ category_color: string;
+ total_quantity: number;
+ total_revenue: number;
+ products: DailyProductItem[];
+}
+export interface DailyDetail {
+ date: string;
+ total_orders: number;
+ total_quantity: number;
+ total_revenue: number;
+ categories: DailyCategoryDetail[];
+}
+
export type StatSection = "commandes" | "revenus" | "produits" | "heures" | "jours" | "doses";
export interface AdminStats {
@@ -114,6 +136,7 @@ export interface AdminStats {
by_hour: HourStat[];
top_products: ProductStat[];
by_quantity: ProductQuantityBreakdown[];
+ daily_detail?: DailyDetail;
reset_at_commandes?: string;
reset_at_revenus?: string;
reset_at_produits?: string;
diff --git a/frontend-admin/src/screens/admin/StatsScreen.tsx b/frontend-admin/src/screens/admin/StatsScreen.tsx
index 88633bb9..1b7e288a 100644
--- a/frontend-admin/src/screens/admin/StatsScreen.tsx
+++ b/frontend-admin/src/screens/admin/StatsScreen.tsx
@@ -15,7 +15,7 @@ import { spacing, fontSize, borderRadius } from "../../theme";
import { shadows } from "../../theme/shadows";
import LoadingSpinner from "../../components/ui/LoadingSpinner";
import { getAdminStats, resetAdminStats } from "../../api/api_admin";
-import type { AdminStats, WeekdayStat, DayStat, DayRevenueStat, HourStat, ProductStat, ProductQuantityBreakdown, StatSection } from "../../api/api_admin";
+import type { AdminStats, WeekdayStat, DayStat, DayRevenueStat, HourStat, ProductStat, ProductQuantityBreakdown, StatSection, DailyDetail, DailyCategoryDetail } from "../../api/api_admin";
// ── Palette graphiques ────────────────────────────────────────────────────────
const CHART_ACCENT = "#6366f1";
@@ -186,6 +186,123 @@ function SectionResetBtn({ onPress, loading, resetAt }: { onPress: () => void; l
);
}
+// ── Section détail du jour ────────────────────────────────────────────────────
+function DailyDetailSection({ daily }: { daily: DailyDetail }) {
+ const { colors } = useTheme();
+ const hasData = daily.categories.length > 0;
+
+ return (
+
+ {/* En-tête */}
+
+
+
+ Activité du jour
+
+ {daily.date}
+
+
+ {/* Mini-résumé */}
+
+
+
+ {daily.total_orders}
+ commandes
+
+
+
+ {daily.total_quantity}g
+ vendues
+
+
+
+ {fmtEuro(daily.total_revenue)}
+ revenus
+
+
+
+ {!hasData ? (
+ Aucune commande aujourd'hui
+ ) : (
+ daily.categories.map((cat: DailyCategoryDetail, ci: number) => {
+ const catMax = Math.max(...cat.products.map((p) => p.quantity), 1);
+ const isLast = ci === daily.categories.length - 1;
+ return (
+
+ {/* En-tête catégorie */}
+
+
+
+ {cat.category}
+
+
+ {cat.total_quantity}g
+ {fmtEuro(cat.total_revenue)}
+
+
+
+ {/* Lignes produits */}
+ {cat.products.map((prod, pi) => {
+ const pct = catMax > 0 ? Math.max((prod.quantity / catMax) * 100, prod.quantity > 0 ? 3 : 0) : 0;
+ const isTopInCat = pi === 0;
+ return (
+
+
+
+ {prod.name}
+
+
+
+ {prod.quantity}g · {prod.order_count} cmd
+
+
+ {fmtEuro(prod.revenue)}
+
+
+
+
+
+
+
+ );
+ })}
+
+ );
+ })
+ )}
+
+ );
+}
+
+const ddStyles = StyleSheet.create({
+ card: { borderRadius: borderRadius.md, padding: spacing.l, marginBottom: spacing.m, borderWidth: 1 },
+ header: { flexDirection: "row", alignItems: "center", justifyContent: "space-between", marginBottom: spacing.m },
+ title: { fontSize: fontSize.md, fontWeight: "600" },
+ date: { fontSize: fontSize.xs, fontWeight: "500" },
+ chipRow: { flexDirection: "row", gap: spacing.s, marginBottom: spacing.l },
+ chip: { flex: 1, alignItems: "center", gap: 3, paddingVertical: spacing.s, borderRadius: borderRadius.sm, borderWidth: 1 },
+ chipVal: { fontSize: fontSize.sm, fontWeight: "700" },
+ chipLbl: { fontSize: 9, fontWeight: "500" },
+ empty: { textAlign: "center", fontSize: fontSize.sm, paddingVertical: spacing.l },
+ catBlock: { marginBottom: spacing.l, paddingBottom: spacing.l, borderBottomWidth: 1 },
+ catHeader: { flexDirection: "row", alignItems: "center", justifyContent: "space-between", marginBottom: spacing.s, paddingBottom: spacing.xs },
+ catDot: { width: 10, height: 10, borderRadius: 5 },
+ catName: { fontSize: fontSize.sm, fontWeight: "700" },
+ catMeta: { fontSize: fontSize.xs },
+ catRevenue: { fontSize: fontSize.xs, fontWeight: "700" },
+ prodRow: { marginBottom: spacing.s },
+ prodTopLine:{ flexDirection: "row", alignItems: "center", justifyContent: "space-between", marginBottom: 4 },
+ prodName: { flex: 1, fontSize: fontSize.xs, marginRight: spacing.s },
+ prodMeta: { flexDirection: "row", alignItems: "center", gap: spacing.m },
+ prodQty: { fontSize: fontSize.xs },
+ prodRevenue:{ fontSize: fontSize.xs, fontWeight: "600", minWidth: 52, textAlign: "right" },
+ bar: { height: 5, borderRadius: 3, overflow: "hidden" },
+ barFill: { height: "100%", borderRadius: 3 },
+});
+
// ── Sélecteur de période top produits ────────────────────────────────────────
type ProdSort = "quantity" | "orders" | "revenue";
@@ -325,6 +442,11 @@ export default function StatsScreen() {
/>
+ {/* ── Activité du jour ── */}
+ {stats?.daily_detail && (
+
+ )}
+
{/* ── Évolution 30 jours (commandes) ── */}