feat(api, i18n): add agent_node_id to various admin queries and enhance multi-language support
Introduced the agent_node_id field in AdminDrawListQuery, AdminPlayerListQuery, AdminSettlementBatchListQuery, TicketItemsListQuery, and TransferOrderListQuery to improve filtering capabilities. Updated the admin-breadcrumb and admin-sidebar components to include new translations for agent-related terms in English, Nepali, and Chinese, enhancing the overall user experience and multi-language support across the admin interface.
This commit is contained in:
@@ -1,16 +1,23 @@
|
||||
"use client";
|
||||
|
||||
import { useCallback, useEffect, useMemo, useState } from "react";
|
||||
import { useCallback, useMemo, useState } from "react";
|
||||
import { useAsyncEffect } from "@/hooks/use-async-effect";
|
||||
import { useTranslationRef } from "@/hooks/use-translation-ref";
|
||||
import { format, subDays } from "date-fns";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
import { getAdminDashboardAnalytics } from "@/api/admin-dashboard";
|
||||
import { useAdminPlayCodeLabel } from "@/hooks/use-admin-play-type-catalog";
|
||||
import { getAdminRequestLocale } from "@/lib/admin-locale";
|
||||
import { formatAdminMinorUnits, getAdminCurrencyDecimalPlaces } from "@/lib/money";
|
||||
import {
|
||||
coerceAdminMinor,
|
||||
formatAdminMinorUnits,
|
||||
getAdminCurrencyDecimalPlaces,
|
||||
} from "@/lib/money";
|
||||
import { LotteryApiBizError } from "@/types/api/errors";
|
||||
import type {
|
||||
AdminDashboardAnalyticsData,
|
||||
AdminDashboardAnalyticsAgentRow,
|
||||
DashboardAnalyticsMetric,
|
||||
DashboardAnalyticsPeriod,
|
||||
} from "@/types/api/admin-dashboard-analytics";
|
||||
@@ -27,9 +34,10 @@ export const DASHBOARD_ANALYTICS_PERIODS: DashboardAnalyticsPeriod[] = [
|
||||
export const DASHBOARD_RANKING_METRICS: DashboardAnalyticsMetric[] = ["bet", "payout", "profit"];
|
||||
|
||||
export function formatDashboardMoneyMinor(minor: number, currencyCode: string | null): string {
|
||||
const safeMinor = coerceAdminMinor(minor);
|
||||
const code = (currencyCode ?? "NPR").toUpperCase();
|
||||
const decimals = getAdminCurrencyDecimalPlaces(code);
|
||||
const major = minor / 10 ** decimals;
|
||||
const major = safeMinor / 10 ** decimals;
|
||||
try {
|
||||
return new Intl.NumberFormat(getAdminRequestLocale(), {
|
||||
style: "currency",
|
||||
@@ -38,7 +46,7 @@ export function formatDashboardMoneyMinor(minor: number, currencyCode: string |
|
||||
maximumFractionDigits: decimals,
|
||||
}).format(major);
|
||||
} catch {
|
||||
return formatAdminMinorUnits(minor, code, decimals);
|
||||
return formatAdminMinorUnits(safeMinor, code, decimals);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -58,6 +66,7 @@ export function useDashboardAnalytics({
|
||||
playOptions: { code: string; label: string }[];
|
||||
}) {
|
||||
const { t } = useTranslation(["dashboard", "common"]);
|
||||
const tRef = useTranslationRef(["dashboard", "common"]);
|
||||
const playLabel = useAdminPlayCodeLabel();
|
||||
|
||||
const [period, setPeriod] = useState<DashboardAnalyticsPeriod>("last_7_days");
|
||||
@@ -94,19 +103,18 @@ export function useDashboardAnalytics({
|
||||
const needsAuthSync =
|
||||
raw.includes("admin.dashboard.analytics") || raw.includes("资源未配置");
|
||||
setError(
|
||||
needsAuthSync ? t("warnings.apiResourceMissing") : raw || t("warnings.loadFailed"),
|
||||
needsAuthSync
|
||||
? tRef.current("warnings.apiResourceMissing")
|
||||
: raw || tRef.current("warnings.loadFailed"),
|
||||
);
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
}, [enabled, period, playCode, customFrom, customTo, t]);
|
||||
}, [enabled, period, playCode, customFrom, customTo]);
|
||||
|
||||
useEffect(() => {
|
||||
const timer = window.setTimeout(() => {
|
||||
void load();
|
||||
}, 0);
|
||||
return () => window.clearTimeout(timer);
|
||||
}, [load]);
|
||||
useAsyncEffect(() => {
|
||||
void load();
|
||||
}, [enabled, period, playCode, customFrom, customTo]);
|
||||
|
||||
const currency = data?.currency_code ?? null;
|
||||
const summary = data?.summary;
|
||||
@@ -152,6 +160,28 @@ export function useDashboardAnalytics({
|
||||
return rows.slice(0, 5);
|
||||
}, [data, rankingMetric]);
|
||||
|
||||
const metricAgentValue = useCallback(
|
||||
(row: AdminDashboardAnalyticsAgentRow): number => {
|
||||
if (rankingMetric === "payout") {
|
||||
return row.total_payout_minor;
|
||||
}
|
||||
if (rankingMetric === "profit") {
|
||||
return row.approx_house_gross_minor;
|
||||
}
|
||||
return row.total_bet_minor;
|
||||
},
|
||||
[rankingMetric],
|
||||
);
|
||||
|
||||
const topAgentRows = useMemo(() => {
|
||||
if (!data) {
|
||||
return [];
|
||||
}
|
||||
const rows = [...data.agent_breakdown];
|
||||
rows.sort((a, b) => metricAgentValue(b) - metricAgentValue(a));
|
||||
return rows.slice(0, 5);
|
||||
}, [data, metricAgentValue]);
|
||||
|
||||
const sparklines = useMemo(() => {
|
||||
const series = data?.daily_series ?? [];
|
||||
return {
|
||||
@@ -183,6 +213,7 @@ export function useDashboardAnalytics({
|
||||
playOptions,
|
||||
resolvePlayLabel,
|
||||
topPlayRows,
|
||||
topAgentRows,
|
||||
sparklines,
|
||||
formatMoney: formatDashboardMoneyMinor,
|
||||
formatSignedMoney: formatDashboardSignedMoneyMinor,
|
||||
|
||||
Reference in New Issue
Block a user