Files
J-Board-Lite/src/app/(user)/subscriptions/_components/proxy-subscription-details.tsx
2026-04-29 05:16:29 +10:00

77 lines
3.6 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { Gauge, Link2, QrCode } from "lucide-react";
import { Progress } from "@/components/ui/progress";
import { CopyButton } from "@/components/shared/copy-button";
import { QrPreview } from "@/components/shared/qr-preview";
import { formatBytes } from "@/lib/utils";
import { buildSingleNodeUri } from "@/services/subscription";
import type { SubscriptionRecord } from "../subscriptions-types";
interface ProxySubscriptionDetailsProps {
sub: SubscriptionRecord;
baseUrl: string;
}
export function ProxySubscriptionDetails({ sub, baseUrl }: ProxySubscriptionDetailsProps) {
if (sub.plan.type !== "PROXY") return null;
const used = Number(sub.trafficUsed);
const limit = sub.trafficLimit ? Number(sub.trafficLimit) : null;
const percent = limit ? Math.min(100, Math.round((used / limit) * 100)) : 0;
const subUrl = `${baseUrl}/api/subscription/${sub.id}?token=${sub.downloadToken}`;
const singleNodeUri = sub.nodeClient ? buildSingleNodeUri(sub.nodeClient) : "";
return (
<div className="space-y-4">
{limit && (
<div className="rounded-lg border border-border bg-muted/30 p-3">
<div className="mb-3 flex flex-wrap items-center justify-between gap-3">
<div className="flex items-center gap-2 text-sm font-semibold">
<Gauge className="size-4 text-primary" />
</div>
<span className="rounded-full bg-primary/10 px-2.5 py-1 text-xs font-semibold text-primary tabular-nums">
{percent}%
</span>
</div>
<Progress value={percent} className="h-2.5" />
<div className="mt-3 grid gap-2 text-xs text-muted-foreground sm:grid-cols-2">
<p className="rounded-xl bg-background/45 px-3 py-2"> <span className="font-semibold text-foreground">{formatBytes(used)}</span></p>
<p className="rounded-xl bg-background/45 px-3 py-2"> <span className="font-semibold text-foreground">{formatBytes(Math.max(0, limit - used))}</span></p>
</div>
</div>
)}
{sub.nodeClient ? (
<div className="rounded-lg border border-border bg-muted/30 p-3">
<div className="flex items-center gap-2 text-sm font-semibold">
<Link2 className="size-4 text-primary" />
</div>
<div className="mt-3 flex flex-col gap-3 rounded-2xl border border-border/40 bg-background/45 p-3 sm:flex-row sm:items-center sm:justify-between">
<div className="min-w-0">
<p className="text-xs font-semibold tracking-[0.14em] text-muted-foreground">SUBSCRIPTION URL</p>
<p className="mt-1 truncate font-mono text-xs text-foreground/82">{subUrl}</p>
</div>
<CopyButton text={subUrl} />
</div>
<div className="mt-3 flex items-center gap-2 text-xs font-semibold tracking-[0.14em] text-muted-foreground">
<QrCode className="size-3.5" />
</div>
<div className="mt-2 grid gap-3 sm:grid-cols-2">
<QrPreview label="订阅 URL 二维码" value={subUrl} alt="订阅 URL 二维码" />
{singleNodeUri && (
<QrPreview
label="单节点 URI 二维码"
value={singleNodeUri}
alt="单节点 URI 二维码"
/>
)}
</div>
</div>
) : (
<div className="rounded-lg border border-dashed border-border bg-muted/20 p-3 text-sm leading-6 text-muted-foreground">
</div>
)}
</div>
);
}