feat(docs, agents, risk): enhance documentation, API queries, and UI components
Updated the public documentation site with improved layout and accessibility, including new sections for client integration and admin guides. Enhanced API queries by adding 'active_only' and 'group_by' parameters for better data filtering in risk management. Refined UI components for agent management, ensuring consistent styling and improved user experience across the application. Added localization support for new documentation content in English and Nepali.
This commit is contained in:
@@ -179,8 +179,9 @@ export function AgentLineDetailPanel({
|
||||
detailTab === "players" ? playerActionHint : childActionHint;
|
||||
|
||||
return (
|
||||
<div className="flex min-h-[28rem] min-w-0 flex-1 flex-col bg-background">
|
||||
<header className="border-b border-border/60 bg-card px-5 py-4 sm:px-6">
|
||||
<div className="flex min-h-0 min-w-0 flex-1 flex-col overflow-hidden bg-background">
|
||||
<div className="shrink-0 bg-card shadow-[0_1px_0_rgb(216_230_251_/_35%)]">
|
||||
<header className="border-b border-border/60 px-5 py-4 sm:px-6">
|
||||
<div className="flex flex-wrap items-start justify-between gap-3">
|
||||
<div className="min-w-0 flex-1">
|
||||
<div className="flex flex-wrap items-center gap-2.5">
|
||||
@@ -232,7 +233,7 @@ export function AgentLineDetailPanel({
|
||||
|
||||
<AdminSubnav
|
||||
aria-label={t("detailTabs", { defaultValue: "代理详情" })}
|
||||
className="overflow-x-auto bg-card px-4 sm:px-5"
|
||||
className="overflow-x-auto border-b border-border/60 px-4 sm:px-5"
|
||||
>
|
||||
{tabs
|
||||
.filter((tab) => tab.visible)
|
||||
@@ -247,8 +248,9 @@ export function AgentLineDetailPanel({
|
||||
</AdminSubnavButton>
|
||||
))}
|
||||
</AdminSubnav>
|
||||
</div>
|
||||
|
||||
<div className="min-h-0 flex-1 overflow-y-auto bg-muted/15 px-5 py-5 sm:px-6 sm:py-6">
|
||||
<div className="min-h-0 flex-1 overflow-y-auto overscroll-contain bg-muted/15 px-5 py-5 sm:px-6 sm:py-6">
|
||||
{detailTab === "overview" ? (
|
||||
<OverviewTab
|
||||
profile={profile}
|
||||
@@ -392,47 +394,42 @@ function OverviewTab({
|
||||
</div>
|
||||
|
||||
{!profileLoading && profile ? (
|
||||
<>
|
||||
<div className="grid gap-3 sm:grid-cols-2 lg:grid-cols-3">
|
||||
<MetricCard
|
||||
label={t("profile.rebateLimit", { defaultValue: "回水上限 (%)" })}
|
||||
value={`${rebateCap ?? "0"}%`}
|
||||
/>
|
||||
<MetricCard
|
||||
label={t("profile.defaultPlayerRebate", { defaultValue: "默认玩家回水 (%)" })}
|
||||
value={`${percentValueToUi(profile.default_player_rebate ?? 0)}%`}
|
||||
/>
|
||||
<MetricCard
|
||||
label={t("profile.riskTags", { defaultValue: "风控标签" })}
|
||||
value={
|
||||
(profile.risk_tags?.length ?? 0) > 0
|
||||
? profile.risk_tags!.join(", ")
|
||||
: t("common:states.none", { defaultValue: "无" })
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="grid gap-3 sm:grid-cols-3">
|
||||
<CapabilityMetric
|
||||
label={t("profile.canGrantExtraRebate", { defaultValue: "允许额外回水" })}
|
||||
enabled={profile.can_grant_extra_rebate === true}
|
||||
yesLabel={yesLabel}
|
||||
noLabel={noLabel}
|
||||
/>
|
||||
<CapabilityMetric
|
||||
label={t("profile.canCreatePlayer", { defaultValue: "允许创建玩家" })}
|
||||
enabled={profile.can_create_player !== false}
|
||||
yesLabel={yesLabel}
|
||||
noLabel={noLabel}
|
||||
/>
|
||||
<CapabilityMetric
|
||||
label={t("profile.canCreateChildAgent", { defaultValue: "允许创建下级代理" })}
|
||||
enabled={profile.can_create_child_agent === true}
|
||||
yesLabel={yesLabel}
|
||||
noLabel={noLabel}
|
||||
/>
|
||||
</div>
|
||||
</>
|
||||
<div className="grid grid-cols-2 gap-3 lg:grid-cols-4">
|
||||
<MetricCard
|
||||
label={t("profile.rebateLimit", { defaultValue: "回水上限 (%)" })}
|
||||
value={`${rebateCap ?? "0"}%`}
|
||||
/>
|
||||
<MetricCard
|
||||
label={t("profile.defaultPlayerRebate", { defaultValue: "默认玩家回水 (%)" })}
|
||||
value={`${percentValueToUi(profile.default_player_rebate ?? 0)}%`}
|
||||
/>
|
||||
<MetricCard
|
||||
label={t("profile.riskTags", { defaultValue: "风控标签" })}
|
||||
value={
|
||||
(profile.risk_tags?.length ?? 0) > 0
|
||||
? profile.risk_tags!.join(", ")
|
||||
: t("common:states.none", { defaultValue: "无" })
|
||||
}
|
||||
/>
|
||||
<CapabilityMetric
|
||||
label={t("profile.canGrantExtraRebate", { defaultValue: "允许额外回水" })}
|
||||
enabled={profile.can_grant_extra_rebate === true}
|
||||
yesLabel={yesLabel}
|
||||
noLabel={noLabel}
|
||||
/>
|
||||
<CapabilityMetric
|
||||
label={t("profile.canCreatePlayer", { defaultValue: "允许创建玩家" })}
|
||||
enabled={profile.can_create_player !== false}
|
||||
yesLabel={yesLabel}
|
||||
noLabel={noLabel}
|
||||
/>
|
||||
<CapabilityMetric
|
||||
label={t("profile.canCreateChildAgent", { defaultValue: "允许创建下级代理" })}
|
||||
enabled={profile.can_create_child_agent === true}
|
||||
yesLabel={yesLabel}
|
||||
noLabel={noLabel}
|
||||
/>
|
||||
</div>
|
||||
) : null}
|
||||
</div>
|
||||
);
|
||||
@@ -454,7 +451,7 @@ function CapabilityMetric({
|
||||
<p className="text-xs font-medium text-muted-foreground">{label}</p>
|
||||
<p
|
||||
className={cn(
|
||||
"mt-1.5 text-lg font-semibold",
|
||||
"mt-1.5 text-2xl font-semibold tracking-tight",
|
||||
enabled ? "text-foreground" : "text-muted-foreground",
|
||||
)}
|
||||
>
|
||||
|
||||
@@ -231,7 +231,7 @@ export function AgentLineSidebar({
|
||||
const hasAnyAgent = displayForest.length > 0;
|
||||
|
||||
return (
|
||||
<aside className="flex h-full min-h-[28rem] w-full flex-col bg-muted/10 lg:w-[18rem] lg:shrink-0 lg:border-r lg:border-border/70">
|
||||
<aside className="flex min-h-0 h-full w-full flex-col bg-muted/10 lg:w-[18rem] lg:shrink-0 lg:border-r lg:border-border/70">
|
||||
<div className="space-y-3 border-b border-border/60 bg-card px-4 py-4">
|
||||
{siteLabel ? (
|
||||
<p className="truncate text-xs font-medium text-foreground/80" title={siteLabel}>
|
||||
|
||||
@@ -23,6 +23,7 @@ import { AdminStatusBadge } from "@/components/admin/admin-status-badge";
|
||||
import {
|
||||
AGENT_PERCENT_HARD_MAX,
|
||||
actualShareRateFromRelative,
|
||||
creditLimitRangeIssue,
|
||||
isNumericStepperOutOfRange,
|
||||
maxCreditLimitFromParent,
|
||||
maxDefaultRebatePercent,
|
||||
@@ -104,6 +105,10 @@ export function AgentProfileFields({
|
||||
const maxDefaultRebate = maxDefaultRebatePercent(rebateLimit, parentCaps);
|
||||
const maxCreditLimit = maxCreditLimitFromParent(parentCaps, baselineCreditLimit);
|
||||
const actualShare = actualShareRateFromRelative(Number.parseFloat(shareRate) || 0, parentCaps);
|
||||
const creditRangeIssue = creditLimitRangeIssue(creditLimit, {
|
||||
min: minCreditLimit,
|
||||
max: maxCreditLimit,
|
||||
});
|
||||
|
||||
return (
|
||||
<div className="space-y-6">
|
||||
@@ -224,19 +229,19 @@ export function AgentProfileFields({
|
||||
})}
|
||||
</p>
|
||||
) : null}
|
||||
{profileScalarsEditable &&
|
||||
isNumericStepperOutOfRange(creditLimit, {
|
||||
min: minCreditLimit,
|
||||
max: maxCreditLimit,
|
||||
integer: true,
|
||||
}) ? (
|
||||
{profileScalarsEditable && creditRangeIssue === "below_min" ? (
|
||||
<p className="text-xs text-destructive">
|
||||
{t("profile.validation.creditBelowAllocated", {
|
||||
defaultValue: "授信额度不能低于已下发给下级/玩家的总额(当前至少 {{min}})",
|
||||
min: formatAdminCreditMajorDecimal(minCreditLimit, currencyCode),
|
||||
})}
|
||||
</p>
|
||||
) : null}
|
||||
{profileScalarsEditable && creditRangeIssue === "above_max" && maxCreditLimit !== undefined ? (
|
||||
<p className="text-xs text-destructive">
|
||||
{t("profile.validation.creditExceedsParentWithMax", {
|
||||
defaultValue: "授信额度不能超过 {{max}}",
|
||||
max:
|
||||
maxCreditLimit !== undefined
|
||||
? formatAdminCreditMajorDecimal(maxCreditLimit, currencyCode)
|
||||
: creditLimit,
|
||||
max: formatAdminCreditMajorDecimal(maxCreditLimit, currencyCode),
|
||||
})}
|
||||
</p>
|
||||
) : null}
|
||||
|
||||
@@ -21,6 +21,8 @@ import { AgentLineProvisionWizard } from "@/modules/agents/agent-line-provision-
|
||||
import { AgentLineSidebar } from "@/modules/agents/agent-line-sidebar";
|
||||
import { AgentProfileFields } from "@/modules/agents/agent-profile-fields";
|
||||
import { AdminLoadingState } from "@/components/admin/admin-loading-state";
|
||||
import { AdminPageGuide } from "@/components/admin/admin-page-guide";
|
||||
import { ADMIN_DOC_LINKS } from "@/lib/admin-doc-links";
|
||||
import { AdminNoIntegrationSiteState } from "@/components/admin/admin-no-integration-site-state";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import {
|
||||
@@ -952,7 +954,7 @@ export function AgentsConsole(): React.ReactElement {
|
||||
|
||||
if (showProvisionEmpty) {
|
||||
return (
|
||||
<div className="flex min-h-[32rem] flex-col gap-0">
|
||||
<div className="flex min-h-0 flex-1 flex-col gap-0">
|
||||
<AgentLineProvisionWizard
|
||||
embedded
|
||||
defaultSiteCode={activeSiteCode}
|
||||
@@ -965,13 +967,14 @@ export function AgentsConsole(): React.ReactElement {
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="flex min-h-[32rem] flex-col gap-0">
|
||||
<div className="flex min-h-0 flex-1 flex-col gap-0">
|
||||
<AdminPageGuide guide={t("pageGuide")} docHref={ADMIN_DOC_LINKS.agents} className="mb-4 px-1" />
|
||||
<ConfirmDialog />
|
||||
|
||||
{canViewAgents && err ? <p className="px-1 text-sm text-destructive">{err}</p> : null}
|
||||
|
||||
{canViewAgents ? (
|
||||
<div className="flex min-h-0 flex-1 flex-col overflow-hidden rounded-2xl border border-border/70 bg-card shadow-sm lg:flex-row">
|
||||
<div className="flex max-h-[calc(100dvh-14rem)] min-h-[28rem] flex-1 flex-col overflow-hidden rounded-2xl border border-border/70 bg-card shadow-sm lg:flex-row">
|
||||
{showAgentSidebar ? (
|
||||
<AgentLineSidebar
|
||||
siteLabel={selectedSiteLabel}
|
||||
|
||||
Reference in New Issue
Block a user