feat: polish internal value displays

This commit is contained in:
JetSprow
2026-04-30 16:38:38 +10:00
parent abc2d4aa72
commit 042c5b34ab
25 changed files with 498 additions and 163 deletions

View File

@@ -9,6 +9,13 @@ import {
DataTableHeaderRow,
DataTableRow,
} from "@/components/shared/data-table";
import {
formatAuditAction,
formatAuditActorRole,
formatAuditMessage,
formatAuditTargetLabel,
formatAuditTargetType,
} from "@/lib/audit-display";
import { formatDate } from "@/lib/utils";
export function AuditLogsTable({ logs }: { logs: AuditLog[] }) {
@@ -37,20 +44,22 @@ export function AuditLogsTable({ logs }: { logs: AuditLog[] }) {
<DataTableCell>
<div className="space-y-1">
<p>{log.actorEmail || "系统"}</p>
<p className="text-xs text-muted-foreground">{log.actorRole || "—"}</p>
<p className="text-xs text-muted-foreground">{formatAuditActorRole(log.actorRole)}</p>
</div>
</DataTableCell>
<DataTableCell className="whitespace-nowrap font-medium">{log.action}</DataTableCell>
<DataTableCell className="whitespace-nowrap font-medium">
{formatAuditAction(log.action)}
</DataTableCell>
<DataTableCell>
<div className="space-y-1">
<p>{log.targetType}</p>
<p>{formatAuditTargetType(log.targetType)}</p>
<p className="text-xs text-muted-foreground">
{log.targetLabel || log.targetId || "—"}
{formatAuditTargetLabel(log)}
</p>
</div>
</DataTableCell>
<DataTableCell className="max-w-xl whitespace-pre-wrap break-words text-muted-foreground">
{log.message}
{formatAuditMessage(log.message)}
</DataTableCell>
</DataTableRow>
))}

View File

@@ -4,6 +4,7 @@ import { AdminFilterBar } from "@/components/admin/filter-bar";
import { PageHeader, PageShell } from "@/components/shared/page-shell";
import { Pagination } from "@/components/shared/pagination";
import { buttonVariants } from "@/components/ui/button";
import { auditActionFilterOptions } from "@/lib/audit-display";
import { AuditLogsTable } from "./_components/audit-logs-table";
import { buildAuditLogExportHref, getAuditLogs } from "./audit-logs-data";
@@ -42,17 +43,7 @@ export default async function AuditLogsPage({
{
name: "action",
value: filters.action,
options: [
{ label: "全部动作前缀", value: "" },
{ label: "user.", value: "user." },
{ label: "order.", value: "order." },
{ label: "subscription.", value: "subscription." },
{ label: "plan.", value: "plan." },
{ label: "service.", value: "service." },
{ label: "node.", value: "node." },
{ label: "task.", value: "task." },
{ label: "risk.", value: "risk." },
],
options: auditActionFilterOptions,
},
]}
/>

View File

@@ -4,6 +4,7 @@ import { ArrowLeft } from "lucide-react";
import { PageHeader, PageShell } from "@/components/shared/page-shell";
import { StatusBadge } from "@/components/shared/status-badge";
import { buttonVariants } from "@/components/ui/button";
import { getNodeStatusLabel } from "@/lib/domain-labels";
import { getNodeDetail } from "./node-detail-data";
import { NodeDetailTabs } from "./_components/node-detail-tabs";
@@ -34,7 +35,7 @@ export default async function NodeDetailPage({
description={`3x-ui · ${node.panelUrl || "未配置面板"}`}
actions={
<StatusBadge tone={node.status === "active" ? "success" : "neutral"}>
{node.status}
{getNodeStatusLabel(node.status)}
</StatusBadge>
}
className="flex-1"

View File

@@ -5,6 +5,7 @@ import { BatchActionBar, BatchActionButton } from "@/components/admin/batch-acti
import { EmptyState } from "@/components/shared/page-shell";
import { StatusBadge } from "@/components/shared/status-badge";
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
import { getNodeStatusLabel } from "@/lib/domain-labels";
import { InboundDeleteButton } from "../inbound-delete-button";
import { InboundDisplayNameForm } from "../inbound-display-name-form";
import { NodeActions } from "../node-actions";
@@ -52,7 +53,7 @@ function NodeCard({ node, siteUrl }: { node: NodeServerRow; siteUrl: string | nu
</div>
<div className="flex flex-wrap items-center gap-2">
<StatusBadge tone={node.status === "active" ? "success" : "neutral"}>
{node.status}
{getNodeStatusLabel(node.status)}
</StatusBadge>
<NodeForm
node={{

View File

@@ -16,6 +16,10 @@ import {
} from "@/actions/admin/settings";
import { toast } from "sonner";
import { getErrorMessage } from "@/lib/errors";
import {
booleanAppSettingLabels,
type BooleanAppSettingField,
} from "@/lib/domain-labels";
interface AppConfig {
siteName: string;
@@ -72,23 +76,9 @@ interface CouponOption {
const selectClassName = "premium-input w-full appearance-none px-3.5 py-2 text-sm outline-none";
type ToggleValues = Record<BooleanSettingField, boolean>;
type ToggleValues = Record<BooleanAppSettingField, boolean>;
const booleanSettingLabels: Record<BooleanSettingField, string> = {
allowRegistration: "开放注册",
emailVerificationRequired: "注册邮箱验证",
requireInviteCode: "邀请码注册",
autoReminderDispatchEnabled: "自动提醒派发",
trafficSyncEnabled: "3x-ui 流量定时同步",
networkRecommendationsEnabled: "三网推荐",
networkInsightsEnabled: "线路体验",
subscriptionRiskEnabled: "风控总控",
subscriptionRiskAutoSuspend: "自动暂停",
nodeAccessRiskEnabled: "节点日志风控",
inviteRewardEnabled: "自动发放奖励",
smtpEnabled: "邮件服务",
smtpSecure: "SSL 直连",
};
const booleanSettingLabels = booleanAppSettingLabels;
function initialToggleValues(config: AppConfig): ToggleValues {
return {