diff --git a/src/components/admin/admin-permission-selector.tsx b/src/components/admin/admin-permission-selector.tsx index fecfca7..0210bdc 100644 --- a/src/components/admin/admin-permission-selector.tsx +++ b/src/components/admin/admin-permission-selector.tsx @@ -4,7 +4,7 @@ import { ChevronDown } from "lucide-react"; import { useMemo, useState } from "react"; import { Checkbox } from "@/components/ui/checkbox"; -import { AdminNoResourceState, AdminTableNoResourceRow } from "@/components/admin/admin-no-resource-state"; +import { AdminNoResourceState } from "@/components/admin/admin-no-resource-state"; import { ScrollArea } from "@/components/ui/scroll-area"; import { cn } from "@/lib/utils"; import type { AdminPermissionCatalogData } from "@/types/api/admin-user"; @@ -18,7 +18,6 @@ type PermissionSelectorProps = { selectableSlugs?: string[] | null; helperText?: string; summaryText?: string; - emptyText: string; heightClassName?: string; }; @@ -31,7 +30,6 @@ export function AdminPermissionSelector({ selectableSlugs = null, helperText, summaryText, - emptyText, heightClassName = "h-[52vh]", }: PermissionSelectorProps): React.ReactElement { const [expandedGroups, setExpandedGroups] = useState>({}); diff --git a/src/components/admin/player-credit-limit-field.tsx b/src/components/admin/player-credit-limit-field.tsx index 3fb7fae..980904d 100644 --- a/src/components/admin/player-credit-limit-field.tsx +++ b/src/components/admin/player-credit-limit-field.tsx @@ -47,7 +47,6 @@ export function PlayerCreditLimitField({ max={maxCredit} step={1000} integer - preserveOverMaxOnBlur /> {parentAvailableCredit !== null ? (

diff --git a/src/hooks/use-admin-site-code-options.ts b/src/hooks/use-admin-site-code-options.ts index 2b37655..5bfff01 100644 --- a/src/hooks/use-admin-site-code-options.ts +++ b/src/hooks/use-admin-site-code-options.ts @@ -1,6 +1,6 @@ "use client"; -import { useCallback, useEffect, useState } from "react"; +import { useCallback, useState } from "react"; import { getAdminIntegrationSites } from "@/api/admin-integration-sites"; import { adminHasAnyPermission } from "@/lib/admin-permissions"; diff --git a/src/hooks/use-async-effect.ts b/src/hooks/use-async-effect.ts index b306214..a4a5a76 100644 --- a/src/hooks/use-async-effect.ts +++ b/src/hooks/use-async-effect.ts @@ -23,5 +23,6 @@ export function useAsyncEffect( return () => { cleanup?.(); }; + // eslint-disable-next-line react-hooks/exhaustive-deps }, deps); } diff --git a/src/i18n/locales/en/agents.json b/src/i18n/locales/en/agents.json index 1753218..4b440d0 100644 --- a/src/i18n/locales/en/agents.json +++ b/src/i18n/locales/en/agents.json @@ -200,6 +200,8 @@ "rebateLimit": "Rebate ceiling (%)", "relativeShareRate": "Share rate (% of parent)", "relativeShareRateValue": "{{rate}}% of parent", + "actualShareRate": "Effective share {{rate}}%", + "relativeShareCapHint": "Up to 100% of parent (parent total share {{parent}}%)", "riskTags": "Risk tags", "riskTagsPlaceholder": "Comma-separated, e.g. overdue, high_turnover", "saveSuccess": "Share and credit settings saved", diff --git a/src/i18n/locales/en/reports.json b/src/i18n/locales/en/reports.json index 5148574..739110a 100644 --- a/src/i18n/locales/en/reports.json +++ b/src/i18n/locales/en/reports.json @@ -273,6 +273,24 @@ "play": "Play name or code", "operator": "Admin account or name" }, + "labels": { + "transferDirection": { + "in": "Transfer in", + "out": "Transfer out" + }, + "transferStatus": { + "processing": "Processing", + "success": "Success", + "failed": "Failed", + "pending_reconcile": "Pending reconcile", + "reversed": "Reversed", + "manually_processed": "Case closed" + }, + "operatorType": { + "admin": "Admin", + "system": "System" + } + }, "items": { "draw_profit": { "title": "Draw bets / payout / P&L", diff --git a/src/i18n/locales/en/settlementCenter.json b/src/i18n/locales/en/settlementCenter.json index c5e160d..cde45d5 100644 --- a/src/i18n/locales/en/settlementCenter.json +++ b/src/i18n/locales/en/settlementCenter.json @@ -146,8 +146,10 @@ }, "reason": { "payment_record": "Bill payment", + "bet": "Bet hold", "bet_hold": "Bet hold", "game_settlement": "Draw settlement", + "bill_settlement": "Period payment", "game_settlement_reversal": "Draw reversal", "game_settlement_win": "Draw settlement credit", "bet_hold_release": "Hold release", diff --git a/src/i18n/locales/en/wallet.json b/src/i18n/locales/en/wallet.json index ac97a04..875dc0a 100644 --- a/src/i18n/locales/en/wallet.json +++ b/src/i18n/locales/en/wallet.json @@ -24,6 +24,9 @@ "transferOutRefund": "Transfer-out refund", "bizBetDeduct": "Bet debit", "bizBetReverse": "Bet reversal", + "bizPrize": "Payout", + "bizRefund": "Refund", + "bizReversal": "Reversal", "bizSettlePayout": "Settlement payout", "bizJackpotPayout": "Jackpot payout", "bizSettlementAdjustment": "Settlement adjustment", diff --git a/src/i18n/locales/ne/reports.json b/src/i18n/locales/ne/reports.json index 5bc6fd4..bbbca09 100644 --- a/src/i18n/locales/ne/reports.json +++ b/src/i18n/locales/ne/reports.json @@ -93,6 +93,24 @@ "wallet_transfer_report": "वालेट ट्रान्सफर", "wallet_txns_daily": "वालेट लेनदेन (दैनिक)" }, + "labels": { + "operatorType": { + "admin": "प्रशासक", + "system": "प्रणाली" + }, + "transferDirection": { + "in": "भित्र", + "out": "बाहिर" + }, + "transferStatus": { + "failed": "असफल", + "manually_processed": "केस बन्द", + "pending_reconcile": "मिलान बाँकी", + "processing": "प्रक्रियामा", + "reversed": "रिभर्स भयो", + "success": "सफल" + } + }, "libraryTitle": "रिपोर्ट प्रकार", "loadFailed": "लोड असफल", "no": "होइन", diff --git a/src/i18n/locales/ne/settlementCenter.json b/src/i18n/locales/ne/settlementCenter.json index 14fd95a..4cef75a 100644 --- a/src/i18n/locales/ne/settlementCenter.json +++ b/src/i18n/locales/ne/settlementCenter.json @@ -144,8 +144,10 @@ }, "reason": { "payment_record": "Bill payment", + "bet": "Bet hold", "bet_hold": "Bet hold", "game_settlement": "Draw settlement", + "bill_settlement": "Period payment", "game_settlement_reversal": "Draw reversal", "game_settlement_win": "Draw settlement credit", "bet_hold_release": "Hold release", diff --git a/src/i18n/locales/ne/wallet.json b/src/i18n/locales/ne/wallet.json index 544d07a..7ffaf55 100644 --- a/src/i18n/locales/ne/wallet.json +++ b/src/i18n/locales/ne/wallet.json @@ -10,6 +10,9 @@ "bizBetDeduct": "बेट कटौती", "bizBetReverse": "बेट उल्टाउने", "bizJackpotPayout": "ज्याकपट भुक्तानी", + "bizPrize": "भुक्तानी", + "bizRefund": "फिर्ता", + "bizReversal": "रिभर्सल", "bizSettlePayout": "बन्दोबस्त भुक्तानी", "bizSettlementAdjustment": "बन्दोबस्त समायोजन", "bizType": "व्यवसाय प्रकार", diff --git a/src/i18n/locales/zh/agents.json b/src/i18n/locales/zh/agents.json index 1aecfd1..54e5d7f 100644 --- a/src/i18n/locales/zh/agents.json +++ b/src/i18n/locales/zh/agents.json @@ -160,6 +160,8 @@ "totalShareRate": "占成比例 (%)", "relativeShareRate": "占成比例(占上级 %)", "relativeShareRateValue": "占上级 {{rate}}%", + "actualShareRate": "实际占成 {{rate}}%", + "relativeShareCapHint": "占上级比例最高 100%(上级总占成 {{parent}}%)", "creditLimit": "授信额度", "rebateLimit": "回水上限 (%)", "defaultPlayerRebate": "默认玩家回水 (%)", diff --git a/src/i18n/locales/zh/reports.json b/src/i18n/locales/zh/reports.json index f0d31aa..429c401 100644 --- a/src/i18n/locales/zh/reports.json +++ b/src/i18n/locales/zh/reports.json @@ -273,6 +273,24 @@ "play": "玩法名称或编码", "operator": "管理员账号或昵称" }, + "labels": { + "transferDirection": { + "in": "转入", + "out": "转出" + }, + "transferStatus": { + "processing": "处理中", + "success": "成功", + "failed": "失败", + "pending_reconcile": "待对账", + "reversed": "已冲正", + "manually_processed": "已结案" + }, + "operatorType": { + "admin": "管理员", + "system": "系统" + } + }, "items": { "draw_profit": { "title": "每期下注/派彩/盈亏", diff --git a/src/i18n/locales/zh/settlementCenter.json b/src/i18n/locales/zh/settlementCenter.json index 8d83182..2b418d8 100644 --- a/src/i18n/locales/zh/settlementCenter.json +++ b/src/i18n/locales/zh/settlementCenter.json @@ -131,9 +131,11 @@ }, "reason": { "payment_record": "账单收付", + "bet": "下注冻结", "bet_hold": "下注冻结", "freezeAmount": "冻结 {{amount}}", "game_settlement": "开奖结算", + "bill_settlement": "账期收付", "game_settlement_reversal": "开奖冲正", "game_settlement_win": "开奖结算入账", "bet_hold_release": "占用释放", diff --git a/src/i18n/locales/zh/wallet.json b/src/i18n/locales/zh/wallet.json index 287427c..8ac8785 100644 --- a/src/i18n/locales/zh/wallet.json +++ b/src/i18n/locales/zh/wallet.json @@ -24,6 +24,9 @@ "transferOutRefund": "转出失败回补", "bizBetDeduct": "下注扣款", "bizBetReverse": "下注冲正", + "bizPrize": "派彩", + "bizRefund": "退本", + "bizReversal": "冲正", "bizSettlePayout": "派彩入账", "bizJackpotPayout": "奖池派彩", "bizSettlementAdjustment": "结算调账", diff --git a/src/lib/agent-profile-caps.ts b/src/lib/agent-profile-caps.ts index f56fced..5c105f1 100644 --- a/src/lib/agent-profile-caps.ts +++ b/src/lib/agent-profile-caps.ts @@ -1,3 +1,4 @@ +import { percentValueToUi } from "@/lib/admin-rate-percent"; import type { AgentParentCaps } from "@/types/api/admin-agent"; /** 是否为站点一级代理(根节点)。 */ @@ -65,6 +66,29 @@ export function maxCreditLimitFromParent( return baseline + extra; } +/** 实际占成 → 占上级比例(0–100);无上级或上级占成为 0 时返回 null。 */ +export function relativeSharePercentFromTotal( + totalShareRate: number | null | undefined, + parentTotalShareRate: number | null | undefined, +): number | null { + const total = Number(totalShareRate); + const parent = Number(parentTotalShareRate); + if (!Number.isFinite(total) || !Number.isFinite(parent) || parent <= 0) { + return null; + } + + return (total / parent) * 100; +} + +/** 实际占成 → 占上级比例 UI 字符串(如 "50")。 */ +export function relativeShareRateUiFromTotal( + totalShareRate: number | null | undefined, + parentTotalShareRate: number | null | undefined, +): string | null { + const relative = relativeSharePercentFromTotal(totalShareRate, parentTotalShareRate); + return relative === null ? null : percentValueToUi(relative); +} + /** 相对占成(占上级 %)换算后的实际占成,不得超过上级总占成。 */ export function actualShareRateFromRelative( relativePercent: number, diff --git a/src/modules/_config/admin-nav-icons.tsx b/src/modules/_config/admin-nav-icons.tsx index 49e2163..61b2278 100644 --- a/src/modules/_config/admin-nav-icons.tsx +++ b/src/modules/_config/admin-nav-icons.tsx @@ -1,7 +1,6 @@ import type { LucideIcon } from "lucide-react"; import { CalendarClock, - CircleDollarSign, ClipboardList, Coins, FileSpreadsheet, diff --git a/src/modules/agents/agent-line-detail-panel.tsx b/src/modules/agents/agent-line-detail-panel.tsx index 84ebd4a..0f1ea95 100644 --- a/src/modules/agents/agent-line-detail-panel.tsx +++ b/src/modules/agents/agent-line-detail-panel.tsx @@ -4,7 +4,7 @@ import { Pencil, Plus, Trash2, Network } from "lucide-react"; import { useTranslation } from "react-i18next"; import { AdminSubnav, AdminSubnavButton } from "@/components/admin/admin-subnav"; -import { AdminNoResourceState, AdminTableNoResourceRow } from "@/components/admin/admin-no-resource-state"; +import { AdminNoResourceState } from "@/components/admin/admin-no-resource-state"; import { AdminLoadingInline } from "@/components/admin/admin-loading-state"; import { AdminStatusBadge } from "@/components/admin/admin-status-badge"; import { AdminRowActionsMenu } from "@/components/admin/admin-row-actions-menu"; @@ -21,6 +21,7 @@ import { AgentProfileFields, type AgentProfileFieldsProps } from "@/modules/agen import { formatCredit } from "@/modules/agents/agent-line-sidebar"; import { Button } from "@/components/ui/button"; import { percentValueToUi } from "@/lib/admin-rate-percent"; +import { relativeShareRateUiFromTotal } from "@/lib/agent-profile-caps"; import { resolveRoleStatusTone } from "@/lib/admin-status-tone"; import { AdminTableMoney, adminMoneyCellClassName } from "@/components/admin/admin-table-money"; import type { AgentNodeRow, AgentProfileRow } from "@/types/api/admin-agent"; @@ -41,7 +42,6 @@ export type AgentLineDetailPanelProps = { detailTab: AgentDetailTab; onDetailTabChange: (tab: AgentDetailTab) => void; canViewProfileTab: boolean; - canEditProfileTab: boolean; canSaveProfileTab: boolean; canViewDownlineTab: boolean; canViewPlayersTab: boolean; @@ -73,7 +73,6 @@ export function AgentLineDetailPanel({ detailTab, onDetailTabChange, canViewProfileTab, - canEditProfileTab, canSaveProfileTab, canViewDownlineTab, canViewPlayersTab, @@ -149,7 +148,7 @@ export function AgentLineDetailPanel({ {metaParts.length > 0 ? (

{metaParts.join(" · ")}

) : null} - + {canManageNode ? (