geo/frontend/components/dashboard/ROICard.tsx

87 lines
2.3 KiB
TypeScript

"use client";
import { cn } from "@/lib/utils";
import { TrendingUp, TrendingDown, Minus } from "lucide-react";
interface ROICardProps {
roiPercentage: number;
valueGenerated: number;
subscriptionCost: number;
className?: string;
}
export function ROICard({
roiPercentage,
valueGenerated,
subscriptionCost,
className,
}: ROICardProps) {
const isPositive = roiPercentage > 0;
const isNeutral = roiPercentage === 0;
const TrendIcon = isNeutral ? Minus : isPositive ? TrendingUp : TrendingDown;
const trendColor = isNeutral
? "text-gray-500"
: isPositive
? "text-emerald-600"
: "text-red-600";
const formatCurrency = (value: number) =>
new Intl.NumberFormat("zh-CN", {
style: "currency",
currency: "CNY",
maximumFractionDigits: 0,
}).format(value);
return (
<div
className={cn(
"bg-white rounded-xl border border-gray-200 p-6",
className
)}
>
<div className="flex items-center justify-between mb-4">
<p className="text-sm font-medium text-gray-500"> (ROI)</p>
<div className={cn("flex items-center gap-1", trendColor)}>
<TrendIcon className="h-4 w-4" />
<span className="text-sm font-medium">
{isPositive ? "+" : ""}
{roiPercentage.toFixed(1)}%
</span>
</div>
</div>
<p className={cn("text-4xl font-bold leading-none", trendColor)}>
{roiPercentage.toFixed(1)}%
</p>
<div className="mt-4 grid grid-cols-2 gap-4">
<div>
<p className="text-xs text-gray-500"></p>
<p className="text-lg font-semibold text-emerald-600">
{formatCurrency(valueGenerated)}
</p>
</div>
<div>
<p className="text-xs text-gray-500"></p>
<p className="text-lg font-semibold text-gray-900">
{formatCurrency(subscriptionCost)}
</p>
</div>
</div>
<div className="mt-4 h-2 w-full rounded-full bg-gray-100 overflow-hidden">
<div
className={cn(
"h-full rounded-full transition-all duration-300",
isPositive ? "bg-emerald-500" : "bg-red-500"
)}
style={{
width: `${Math.min(Math.abs(roiPercentage), 100)}%`,
}}
/>
</div>
</div>
);
}