+
{label}
+
+
0 ? '4px' : '0' }} />
+
+
{value}
+
+ );
+};
+
+export const PickupStatsPanel = ({ stats, isLoading, mobile, fontSize }) => {
+ if (isLoading) {
+ return (
+
+
+ Загрузка статистики самовывоза...
+
+
+ );
+ }
+
+ if (!stats) {
+ return (
+
+
+ Нет данных по самовывозу
+
+
+ );
+ }
+
+ const fs = fontSize || { xs: '0.65rem', s: '0.68rem', m: '0.78rem', l: '0.85rem', xl: '1.1rem' };
+ const totalPickups = Number(stats.total_pickups) || 0;
+ const pickupRate = Number(stats.pickup_rate) || 0;
+ const avgDays = stats.avg_days_until_pickup != null ? Number(stats.avg_days_until_pickup) : null;
+ const dist = stats.delivery_type_dist || {};
+
+ const maxDay = Math.max(
+ Number(stats.pickup_today) || 0,
+ Number(stats.pickup_tomorrow) || 0,
+ Number(stats.pickup_day_after) || 0,
+ 1
+ );
+
+ const maxHalf = Math.max(
+ Number(stats.pickup_first_half) || 0,
+ Number(stats.pickup_second_half) || 0,
+ 1
+ );
+
+ const pieData = [
+ { name: 'Самовывоз', value: Number(dist.pickup) || 0, fill: PICKUP_COLORS.pickup },
+ { name: 'Доставка', value: Number(dist.delivery) || 0, fill: PICKUP_COLORS.delivery },
+ ].filter(d => d.value > 0);
+
+ return (
+
+
+ 📦 Самовывоз
+
+
+ {/* KPI row */}
+
+ {[
+ { label: 'Всего самовывоз', val: totalPickups, color: '#f59e0b' },
+ { label: 'Доля самовывоза', val: pickupRate + '%', color: '#f59e0b' },
+ { label: 'Ср. дней до выдачи', val: avgDays !== null ? avgDays : '—', color: '#3b82f6' },
+ ].map((kpi, i) => (
+
+
{kpi.label}
+
{kpi.val}
+
+ ))}
+
+
+ {/* Distribution by day */}
+
+
+ {/* Half-day split */}
+
+
+ {/* Saturday */}
+
+ Самовывоз в субботу
+ {Number(stats.pickup_on_saturday) || 0}
+
+
+ {/* Delivery vs Pickup donut */}
+ {pieData.length > 0 && (
+
+
Доставка vs Самовывоз
+
+
+
+ {pieData.map((entry, i) => (
+ |
+ ))}
+
+ } />
+
+
+
+ {pieData.map((d, i) => (
+
+
+
{d.name}: {d.value}
+
+ ))}
+
+
+ )}
+
+ );
+};
diff --git a/src/hooks/usePickupStats.js b/src/hooks/usePickupStats.js
new file mode 100644
index 0000000..5cb31a1
--- /dev/null
+++ b/src/hooks/usePickupStats.js
@@ -0,0 +1,31 @@
+import { useState, useEffect, useCallback } from "react";
+import { supabase, hasSupabaseConfig } from "../supabaseClient";
+
+const PERIOD_DAYS = { today: 1, "7d": 7, "30d": 30, all: 0 };
+
+export const usePickupStats = (period = "7d") => {
+ const [stats, setStats] = useState(null);
+ const [isLoading, setIsLoading] = useState(true);
+ const [error, setError] = useState(null);
+
+ const days = PERIOD_DAYS[period] ?? 7;
+
+ const fetchStats = useCallback(async () => {
+ setIsLoading(true);
+ setError(null);
+ try {
+ if (!hasSupabaseConfig || !supabase) throw new Error("Supabase не сконфигурирован");
+ const { data, error: rpcError } = await supabase.rpc("admin_pickup_stats", { p_days: days, p_date_from: null, p_date_to: null });
+ if (rpcError) throw rpcError;
+ setStats(data?.[0] || null);
+ } catch (e) {
+ setError(e.message || "Ошибка загрузки статистики самовывоза");
+ } finally {
+ setIsLoading(false);
+ }
+ }, [days]);
+
+ useEffect(() => { fetchStats(); }, [fetchStats]);
+
+ return { stats, isLoading, error, refetch: fetchStats };
+};