"use client";
import { useCallback, useMemo, useState, type ReactElement } from "react";
import { useTranslation } from "react-i18next";
import { BarChart3, RefreshCw, TrendingUp, Users, Wallet } from "lucide-react";
import { getAdminDashboard } from "@/api/admin-dashboard";
import { useAsyncEffect } from "@/hooks/use-async-effect";
import { useAdminDateTimeFormatter } from "@/hooks/use-admin-datetime-formatter";
import { useTranslationRef } from "@/hooks/use-translation-ref";
import { useCachedPlayTypeOptions } from "@/hooks/use-cached-play-type-options";
import { adminHasAnyPermission } from "@/lib/admin-permissions";
import { PRD_REPORTS_VIEW_ACCESS_ANY } from "@/lib/admin-prd";
import { normalizeAdminLanguage } from "@/i18n";
import { adminWeekdayKeyForDate, formatAdminCalendarToday } from "@/lib/admin-datetime";
import { signedMoneyClass } from "@/lib/admin-signed-money";
import { cn } from "@/lib/utils";
import { useAdminProfile } from "@/stores/admin-session";
import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert";
import { Button } from "@/components/ui/button";
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
import { Skeleton } from "@/components/ui/skeleton";
import { DashboardCurrentDrawCard } from "@/modules/dashboard/dashboard-current-draw-card";
import { DashboardAnalyticsPanel } from "@/modules/dashboard/dashboard-analytics-panel";
import { DashboardKpiCard } from "@/modules/dashboard/dashboard-visuals";
import {
formatDashboardMoneyMinor,
formatDashboardSignedMoneyMinor,
} from "@/modules/dashboard/use-dashboard-analytics";
import type { AdminDashboardSiteOverview } from "@/types/api/admin-dashboard";
import type { DrawCurrentSnapshot } from "@/types/api/public-draw";
import { LotteryApiBizError } from "@/types/api/errors";
function SiteMetric({
label,
value,
}: {
label: string;
value: string;
}): ReactElement {
return (
);
}
export function SiteDashboardConsole(): ReactElement {
const { t, i18n } = useTranslation(["dashboard", "common"]);
const tRef = useTranslationRef(["dashboard", "common"]);
const formatDt = useAdminDateTimeFormatter();
const profile = useAdminProfile();
const site = profile?.site ?? null;
const permissions = useMemo(() => profile?.permissions ?? [], [profile?.permissions]);
const todayLabel = useMemo(() => {
const locale = normalizeAdminLanguage(i18n.resolvedLanguage ?? i18n.language);
const weekday = t(`date.weekdays.${adminWeekdayKeyForDate()}`, { ns: "common" });
return formatAdminCalendarToday(locale, weekday);
}, [i18n.language, i18n.resolvedLanguage, t]);
const playOptions = useCachedPlayTypeOptions();
const [loading, setLoading] = useState(true);
const [refreshing, setRefreshing] = useState(false);
const [error, setError] = useState(null);
const [hall, setHall] = useState(null);
const [drawId, setDrawId] = useState(null);
const [overview, setOverview] = useState(null);
const analyticsScope = useMemo(
() => ({
siteCode: site?.code ?? overview?.site_code ?? "",
agentNodeId: undefined,
}),
[overview?.site_code, site?.code],
);
const canAnalytics = adminHasAnyPermission(permissions, [...PRD_REPORTS_VIEW_ACCESS_ANY]);
const load = useCallback(async (isRefresh = false) => {
if (isRefresh) {
setRefreshing(true);
} else {
setLoading(true);
}
setError(null);
try {
const d = await getAdminDashboard();
setHall(d.hall);
setOverview(d.site_overview);
if (d.resolved_draw != null) {
setDrawId(d.resolved_draw.id);
} else {
setDrawId(null);
}
} catch (e) {
const msg =
e instanceof LotteryApiBizError ? e.message : tRef.current("warnings.loadFailed");
setError(msg);
} finally {
setLoading(false);
setRefreshing(false);
}
}, [tRef]);
useAsyncEffect(() => {
void load(false);
}, []);
const displayCurrency = overview?.currency_code ?? "NPR";
return (
{t("site.title")}
{site
? t("site.subtitle", { name: site.name || site.code })
: todayLabel}
{error ? (
{t("notice")}
{error}
) : null}
{loading ? (
{Array.from({ length: 4 }).map((_, i) => (
))}
) : overview ? (
}
hint={
overview.latest_bet_at
? t("site.latestBetAt", { time: formatDt(overview.latest_bet_at) })
: t("site.noBetToday")
}
/>
}
hint={t("site.profitScopeHint")}
valueClassName={signedMoneyClass(overview.today_profit_minor, true)}
/>
}
hint={t("site.betOrdersTodayHint", { count: overview.bet_order_count_today })}
/>
}
hint={t("site.pendingUnpaid", {
amount: formatDashboardMoneyMinor(overview.pending_unpaid_minor, displayCurrency),
})}
accent={overview.pending_bill_count > 0 ? "destructive" : "muted"}
/>
{t("site.sevenDayTitle")}
{t("site.todayBet")}
{formatDashboardMoneyMinor(overview.seven_day_bet_minor, displayCurrency)}
{t("site.sevenDayProfit")}
{formatDashboardSignedMoneyMinor(overview.seven_day_profit_minor, displayCurrency)}
{t("site.scaleTitle")}
{overview.top_agent_today ? (
{t("site.topAgentToday", {
name: overview.top_agent_today.agent_name || overview.top_agent_today.agent_code,
amount: formatDashboardMoneyMinor(
overview.top_agent_today.total_bet_minor,
displayCurrency,
),
})}
) : null}
) : null}
{canAnalytics ? (
) : null}
);
}