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:
2026-06-02 14:37:08 +08:00
parent a4e7a2d228
commit b15e377187
105 changed files with 5305 additions and 1596 deletions

View File

@@ -19,7 +19,14 @@ import {
ConfigVersionToolbarMeta,
ConfigVersionToolbarMetaEmphasis,
} from "@/modules/config/config-version-toolbar-meta";
import { getAdminSettings, updateAdminSetting } from "@/api/admin-settings";
import { updateAdminSetting } from "@/api/admin-settings";
import { useAsyncEffect } from "@/hooks/use-async-effect";
import { useTranslationRef } from "@/hooks/use-translation-ref";
import {
loadApplyRebateToPayoutSetting,
setCachedApplyRebateToPayoutSetting,
} from "@/lib/admin-settlement-settings-cache";
import { ensureAdminPlayTypesLoaded } from "@/lib/admin-play-types";
import { Alert, AlertDescription } from "@/components/ui/alert";
import { Button } from "@/components/ui/button";
import { Switch } from "@/components/ui/switch";
@@ -33,6 +40,7 @@ import {
} from "@/components/ui/dialog";
import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label";
import { AdminLoadingState, AdminLoadingInline, AdminTableLoadingRow } from "@/components/admin/admin-loading-state";
import { ConfigReadonlyValue } from "@/modules/config/config-readonly-value";
import { ConfigVersionActions } from "@/modules/config/config-version-actions";
import { ConfigVersionSwitcher } from "@/modules/config/config-version-switcher";
@@ -58,7 +66,6 @@ import {
} from "@/modules/config/doc/odds-rebate-rates";
import { PRIZE_SCOPE_ORDER } from "@/modules/config/doc/prize-scopes";
const SETTLEMENT_GROUP = "settlement";
const APPLY_REBATE_TO_PAYOUT_KEY = "settlement.apply_rebate_to_payout";
function dimensionDistinctPrimaryScopePercents(
@@ -98,6 +105,7 @@ export function RebateConfigDocScreen({
onVersionIdChange,
}: RebateConfigDocScreenProps) {
const { t } = useTranslation(["config", "common"]);
const tRef = useTranslationRef(["config", "common"]);
const { request: requestConfirm, ConfirmDialog } = useConfirmAction();
const profile = useAdminProfile();
const canManage = adminHasAnyPermission(profile?.permissions, [PRD_REBATE_MANAGE]);
@@ -137,54 +145,52 @@ export function RebateConfigDocScreen({
const refreshTypes = useCallback(async () => {
try {
const d = await getAdminPlayTypes();
setTypes(d.items);
setTypes(await ensureAdminPlayTypesLoaded(getAdminPlayTypes));
} catch (e) {
toast.error(e instanceof LotteryApiBizError ? e.message : t("errors.loadFailed", { ns: "common" }));
toast.error(
e instanceof LotteryApiBizError ? e.message : tRef.current("errors.loadFailed", { ns: "common" }),
);
setTypes([]);
}
}, [t]);
}, []);
const refreshList = useCallback(async () => {
try {
const d = await getAllConfigVersions(getOddsVersions);
setListRows(d.items);
} catch (e) {
toast.error(e instanceof LotteryApiBizError ? e.message : t("errors.loadFailed", { ns: "common" }));
toast.error(
e instanceof LotteryApiBizError ? e.message : tRef.current("errors.loadFailed", { ns: "common" }),
);
setListRows([]);
}
}, [t]);
}, []);
const loadWinEnjoySetting = useCallback(async () => {
setWinEnjoyLoading(true);
try {
const res = await getAdminSettings(SETTLEMENT_GROUP);
const hit = res.items.find((item) => item.key === APPLY_REBATE_TO_PAYOUT_KEY);
setApplyRebateToPayout(Boolean(hit?.value));
} catch (e) {
toast.error(e instanceof LotteryApiBizError ? e.message : t("errors.loadFailed", { ns: "common" }));
} finally {
setWinEnjoyLoading(false);
}
}, [t]);
useEffect(() => {
useAsyncEffect(() => {
if (workspace) {
return;
}
queueMicrotask(async () => {
void (async () => {
setLoading(true);
await refreshTypes();
await refreshList();
await Promise.all([refreshTypes(), refreshList()]);
setLoading(false);
});
}, [refreshTypes, refreshList, workspace]);
})();
}, [workspace]);
useEffect(() => {
queueMicrotask(() => {
void loadWinEnjoySetting();
});
}, [loadWinEnjoySetting]);
useAsyncEffect(() => {
void (async () => {
setWinEnjoyLoading(true);
try {
setApplyRebateToPayout(await loadApplyRebateToPayoutSetting());
} catch (e) {
toast.error(
e instanceof LotteryApiBizError ? e.message : tRef.current("errors.loadFailed", { ns: "common" }),
);
} finally {
setWinEnjoyLoading(false);
}
})();
}, []);
useEffect(() => {
if (!workspace) {
@@ -202,6 +208,7 @@ export function RebateConfigDocScreen({
setWinEnjoySaving(true);
try {
await updateAdminSetting(APPLY_REBATE_TO_PAYOUT_KEY, checked);
setCachedApplyRebateToPayoutSetting(checked);
setApplyRebateToPayout(checked);
toast.success(t("rebate.winEnjoy.saveSuccess", { ns: "config" }));
} catch (e) {
@@ -214,8 +221,7 @@ export function RebateConfigDocScreen({
const loadDetail = useCallback(async (id: number) => {
setLoadingDetail(true);
try {
const pt = await getAdminPlayTypes();
const typeList = pt.items;
const typeList = await ensureAdminPlayTypesLoaded(getAdminPlayTypes);
setTypes(typeList);
const d = await getOddsVersion(id);
const rows = d.items.map((it) => ({ ...it }));
@@ -225,13 +231,15 @@ export function RebateConfigDocScreen({
setP3(inferRebatePercentFromDimension(3, rows, typeList));
setP4(inferRebatePercentFromDimension(4, rows, typeList));
} catch (e) {
toast.error(e instanceof LotteryApiBizError ? e.message : t("errors.loadFailed", { ns: "common" }));
toast.error(
e instanceof LotteryApiBizError ? e.message : tRef.current("errors.loadFailed", { ns: "common" }),
);
setDetail(null);
setDraftRows([]);
} finally {
setLoadingDetail(false);
}
}, [t]);
}, []);
useEffect(() => {
if (workspace) {
@@ -614,7 +622,7 @@ export function RebateConfigDocScreen({
) : null}
{resolvedLoading || resolvedLoadingDetail ? (
<p className="text-sm text-muted-foreground">{t("states.loading", { ns: "common" })}</p>
<AdminLoadingState minHeight="6rem" className="py-6" />
) : null}
</>
);