feat(settlement, admin): introduce new types and functions for downline share and settlement period hints
Added new types for downline share breakdown and settlement period open hints to enhance the agent settlement API. Updated the admin console components to support these new features, improving the user experience with better data presentation and interaction. Additionally, refined the date range field to accommodate new calendar markers and hints, ensuring a more intuitive interface for managing settlement periods.
This commit is contained in:
@@ -1,7 +1,6 @@
|
||||
"use client";
|
||||
|
||||
import Link from "next/link";
|
||||
import { RefreshCw, Search } from "lucide-react";
|
||||
import { Eye, RefreshCw, Search } from "lucide-react";
|
||||
import { useCallback, useMemo, useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
@@ -9,11 +8,10 @@ import { getAgentNodes } from "@/api/admin-agents";
|
||||
import { AdminPageCard } from "@/components/admin/admin-page-card";
|
||||
import { AdminTableLoadingRow } from "@/components/admin/admin-loading-state";
|
||||
import { AdminTableNoResourceRow } from "@/components/admin/admin-no-resource-state";
|
||||
import { AdminRowActionsMenu } from "@/components/admin/admin-row-actions-menu";
|
||||
import { AdminStatusBadge } from "@/components/admin/admin-status-badge";
|
||||
import { Button, buttonVariants } from "@/components/ui/button";
|
||||
import { Checkbox } from "@/components/ui/checkbox";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Input } from "@/components/ui/input";
|
||||
import { Label } from "@/components/ui/label";
|
||||
import {
|
||||
Select,
|
||||
SelectContent,
|
||||
@@ -32,7 +30,6 @@ import {
|
||||
import { useAsyncEffect } from "@/hooks/use-async-effect";
|
||||
import { useTranslationRef } from "@/hooks/use-translation-ref";
|
||||
import type { AgentNodeRow } from "@/types/api/admin-agent";
|
||||
import { cn } from "@/lib/utils";
|
||||
|
||||
function formatPercent(value: number | null | undefined): string {
|
||||
if (value == null || Number.isNaN(value)) {
|
||||
@@ -56,6 +53,22 @@ function statusLabel(status: number, t: (key: string, options?: { defaultValue?:
|
||||
: t("statusDisabled", { defaultValue: "停用" });
|
||||
}
|
||||
|
||||
type DirectoryStatusFilter = "all" | "enabled" | "disabled";
|
||||
|
||||
function directoryStatusLabel(
|
||||
value: DirectoryStatusFilter,
|
||||
t: (key: string, options?: { defaultValue?: string }) => string,
|
||||
): string {
|
||||
switch (value) {
|
||||
case "enabled":
|
||||
return t("directoryStatus.enabled", { defaultValue: "仅启用" });
|
||||
case "disabled":
|
||||
return t("directoryStatus.disabled", { defaultValue: "仅停用" });
|
||||
default:
|
||||
return t("directoryStatus.all", { defaultValue: "全部状态" });
|
||||
}
|
||||
}
|
||||
|
||||
export function AgentsDirectoryConsole(): React.ReactElement {
|
||||
const { t } = useTranslation(["agents", "common"]);
|
||||
const tRef = useTranslationRef(["agents", "common"]);
|
||||
@@ -64,8 +77,7 @@ export function AgentsDirectoryConsole(): React.ReactElement {
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [err, setErr] = useState<string | null>(null);
|
||||
const [keyword, setKeyword] = useState("");
|
||||
const [status, setStatus] = useState<"all" | "enabled" | "disabled">("all");
|
||||
const [includeRoots, setIncludeRoots] = useState(false);
|
||||
const [status, setStatus] = useState<DirectoryStatusFilter>("all");
|
||||
const [reloadKey, setReloadKey] = useState(0);
|
||||
|
||||
const parentNameMap = useMemo(
|
||||
@@ -96,9 +108,6 @@ export function AgentsDirectoryConsole(): React.ReactElement {
|
||||
const normalized = keyword.trim().toLowerCase();
|
||||
|
||||
return items.filter((item) => {
|
||||
if (!includeRoots && item.is_root) {
|
||||
return false;
|
||||
}
|
||||
if (status === "enabled" && item.status !== 1) {
|
||||
return false;
|
||||
}
|
||||
@@ -115,7 +124,7 @@ export function AgentsDirectoryConsole(): React.ReactElement {
|
||||
.toLowerCase()
|
||||
.includes(normalized);
|
||||
});
|
||||
}, [includeRoots, items, keyword, parentNameMap, status]);
|
||||
}, [items, keyword, parentNameMap, status]);
|
||||
|
||||
const totalOperatingAgents = useMemo(
|
||||
() => items.filter((item) => !item.is_root).length,
|
||||
@@ -175,29 +184,21 @@ export function AgentsDirectoryConsole(): React.ReactElement {
|
||||
/>
|
||||
</div>
|
||||
<div className="flex flex-wrap items-center gap-3">
|
||||
<Select value={status} onValueChange={(value) => setStatus(value as typeof status)}>
|
||||
<Select
|
||||
value={status}
|
||||
onValueChange={(value) => setStatus((value ?? "all") as DirectoryStatusFilter)}
|
||||
>
|
||||
<SelectTrigger className="h-9 w-[150px]">
|
||||
<SelectValue />
|
||||
<SelectValue>{() => directoryStatusLabel(status, t)}</SelectValue>
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem value="all">
|
||||
{t("directoryStatus.all", { defaultValue: "全部状态" })}
|
||||
</SelectItem>
|
||||
<SelectItem value="enabled">
|
||||
{t("directoryStatus.enabled", { defaultValue: "仅启用" })}
|
||||
</SelectItem>
|
||||
<SelectItem value="disabled">
|
||||
{t("directoryStatus.disabled", { defaultValue: "仅停用" })}
|
||||
</SelectItem>
|
||||
{(["all", "enabled", "disabled"] as DirectoryStatusFilter[]).map((value) => (
|
||||
<SelectItem key={value} value={value}>
|
||||
{directoryStatusLabel(value, t)}
|
||||
</SelectItem>
|
||||
))}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
<Label className="flex h-9 items-center gap-2 rounded-md border border-border/70 px-3 text-sm font-normal">
|
||||
<Checkbox
|
||||
checked={includeRoots}
|
||||
onCheckedChange={(checked) => setIncludeRoots(checked === true)}
|
||||
/>
|
||||
{t("includeRoots", { defaultValue: "包含根节点" })}
|
||||
</Label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -229,8 +230,8 @@ export function AgentsDirectoryConsole(): React.ReactElement {
|
||||
<TableHead className="w-[130px] text-right">
|
||||
{t("lineUi.availableCredit", { defaultValue: "可下发" })}
|
||||
</TableHead>
|
||||
<TableHead className="w-[110px] text-right">
|
||||
{t("common:actions.title", { defaultValue: "操作" })}
|
||||
<TableHead className="sticky right-0 z-20 w-14 bg-muted text-center shadow-[-1px_0_0_rgba(203,213,225,0.7)]">
|
||||
{t("common:table.actions", { defaultValue: "操作" })}
|
||||
</TableHead>
|
||||
</TableRow>
|
||||
</TableHeader>
|
||||
@@ -287,13 +288,17 @@ export function AgentsDirectoryConsole(): React.ReactElement {
|
||||
<TableCell className="text-right">
|
||||
<span className="tabular-nums">{formatCredit(profile?.available_credit)}</span>
|
||||
</TableCell>
|
||||
<TableCell className="text-right">
|
||||
<Link
|
||||
href={`/admin/agents?agent_node_id=${item.id}`}
|
||||
className={cn(buttonVariants({ variant: "ghost", size: "sm" }))}
|
||||
>
|
||||
{t("common:actions.view", { defaultValue: "查看" })}
|
||||
</Link>
|
||||
<TableCell className="sticky right-0 z-10 bg-card text-center shadow-[-1px_0_0_rgba(203,213,225,0.7)]">
|
||||
<AdminRowActionsMenu
|
||||
actions={[
|
||||
{
|
||||
key: "view",
|
||||
label: t("common:actions.viewDetails", { defaultValue: "查看详情" }),
|
||||
icon: Eye,
|
||||
href: `/admin/agents?agent_node_id=${item.id}`,
|
||||
},
|
||||
]}
|
||||
/>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user