"use client"; import Link from "next/link"; import type { ReactNode } from "react"; import { useTranslation } from "react-i18next"; import { BarChart3, Gift, TrendingUp, Wallet } from "lucide-react"; import { AdminDateRangeField } from "@/components/admin/admin-date-range-field"; import { Alert, AlertDescription } from "@/components/ui/alert"; import { buttonVariants } from "@/components/ui/button"; import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; import { Label } from "@/components/ui/label"; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from "@/components/ui/select"; import { Skeleton } from "@/components/ui/skeleton"; import { getAdminRequestLocale } from "@/lib/admin-locale"; import { cn } from "@/lib/utils"; import { DashboardKpiCard } from "@/modules/dashboard/dashboard-visuals"; import { DailyTrendChart, PlayBreakdownChart, } from "@/modules/dashboard/dashboard-trend-charts"; import { DASHBOARD_ANALYTICS_PERIODS, DASHBOARD_RANKING_METRICS, useDashboardAnalytics, type DashboardAnalyticsState, } from "@/modules/dashboard/use-dashboard-analytics"; function computeDeltaPercent(series: number[]): string | null { if (series.length < 2) { return null; } const prev = series[series.length - 2]; const last = series[series.length - 1]; if (prev === 0) { return null; } const pct = ((last - prev) / Math.abs(prev)) * 100; const sign = pct >= 0 ? "▲" : "▼"; return `${sign} ${Math.abs(pct).toFixed(1)}%`; } function deltaClassName(series: number[]): string { if (series.length < 2) { return "text-muted-foreground"; } const last = series[series.length - 1]; const prev = series[series.length - 2]; if (last >= prev) { return "text-emerald-600 dark:text-emerald-400"; } return "text-destructive"; } export function DashboardAnalyticsMain({ analytics }: { analytics: DashboardAnalyticsState }): ReactNode { const { t } = useTranslation(["dashboard", "common"]); const { enabled, period, setPeriod, playCode, setPlayCode, customFrom, setCustomFrom, customTo, setCustomTo, loading, error, data, currency, summary, periodRangeLabel, playFilterLabel, playOptions, sparklines, formatMoney, formatSignedMoney, } = analytics; if (!enabled) { return null; } return (
{t("analytics.title")} {t("viewReports")}
{DASHBOARD_ANALYTICS_PERIODS.map((p) => ( ))}
{period === "custom" ? ( { setCustomFrom(from); setCustomTo(to); }} /> ) : (

{periodRangeLabel ? t("analytics.rangeHint", { range: periodRangeLabel }) : t("analytics.selectPeriod")}

)}
{error ? ( {error} ) : null} {data?.chart_meta.truncated ? (

{t("analytics.chartTruncated", { from: data.chart_meta.chart_date_from, to: data.chart_meta.chart_date_to, days: data.chart_meta.span_days, })}

) : null} {loading ? (
{Array.from({ length: 3 }).map((_, i) => ( ))}
) : summary ? (
} sparklineValues={sparklines.bet} deltaLabel={ computeDeltaPercent(sparklines.bet) ? ( {computeDeltaPercent(sparklines.bet)} ) : undefined } /> 0 ? t("payoutRateOfBet", { rate: ((summary.total_payout_minor / summary.total_bet_minor) * 100).toFixed(1), }) : undefined } icon={} accent="destructive" sparklineValues={sparklines.payout} deltaLabel={ computeDeltaPercent(sparklines.payout) ? ( {computeDeltaPercent(sparklines.payout)} ) : undefined } /> 0 ? t("marginRate", { rate: ((summary.approx_house_gross_minor / summary.total_bet_minor) * 100).toFixed(1), }) : undefined } icon={} sparklineValues={sparklines.profit} deltaLabel={ computeDeltaPercent(sparklines.profit) ? ( {computeDeltaPercent(sparklines.profit)} ) : undefined } />
) : null}

{t("analytics.dailyTrend")}

{t("analytics.granularityDay")}
{loading ? ( ) : data ? ( ) : (

{t("states.noData", { ns: "common" })}

)}
); } export function DashboardPlayRankingCard({ analytics, }: { analytics: DashboardAnalyticsState; }): ReactNode { const { t } = useTranslation(["dashboard", "common"]); const { enabled, rankingMetric, setRankingMetric, period, setPeriod, loading, data, currency, topPlayRows, resolvePlayLabel, formatMoney, } = analytics; if (!enabled) { return null; } return ( {t("analytics.playRanking")}
{DASHBOARD_RANKING_METRICS.map((m) => ( ))}
{loading ? ( ) : data && topPlayRows.length > 0 ? ( ) : (

{t("analytics.noPlayData")}

)}
); } /** 单列堆叠布局(兼容旧用法) */ export function DashboardAnalyticsPanel({ enabled, playOptions, }: { enabled: boolean; playOptions: { code: string; label: string }[]; }): ReactNode { const analytics = useDashboardAnalytics({ enabled, playOptions }); return (
); }