- Add billing-usage feature module with types, API clients, hooks, and components - Create PlanCard, PlanSelector, SubscriptionStatusBadge, InvoiceList, UsageSummaryCard, CouponInput components - Add BillingPage, PlansPage, InvoicesPage, UsagePage - Update routes to include billing section Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
73 lines
2.0 KiB
TypeScript
73 lines
2.0 KiB
TypeScript
import React from 'react';
|
|
|
|
interface UsageProgressBarProps {
|
|
label: string;
|
|
current: number;
|
|
limit: number;
|
|
unit?: string;
|
|
showOverage?: boolean;
|
|
}
|
|
|
|
export const UsageProgressBar: React.FC<UsageProgressBarProps> = ({
|
|
label,
|
|
current,
|
|
limit,
|
|
unit = '',
|
|
showOverage = true,
|
|
}) => {
|
|
const isUnlimited = limit === -1;
|
|
const percent = isUnlimited ? 0 : Math.min((current / limit) * 100, 100);
|
|
const overage = !isUnlimited && current > limit ? current - limit : 0;
|
|
|
|
const getColorClass = () => {
|
|
if (isUnlimited) return 'bg-gray-400';
|
|
if (percent >= 100) return 'bg-red-500';
|
|
if (percent >= 80) return 'bg-yellow-500';
|
|
return 'bg-blue-500';
|
|
};
|
|
|
|
const formatValue = (value: number) => {
|
|
if (value >= 1000000) return `${(value / 1000000).toFixed(1)}M`;
|
|
if (value >= 1000) return `${(value / 1000).toFixed(1)}K`;
|
|
return value.toString();
|
|
};
|
|
|
|
return (
|
|
<div className="space-y-1">
|
|
<div className="flex items-center justify-between text-sm">
|
|
<span className="font-medium text-gray-700">{label}</span>
|
|
<span className="text-gray-500">
|
|
{formatValue(current)}
|
|
{unit && ` ${unit}`}
|
|
{!isUnlimited && (
|
|
<>
|
|
{' / '}
|
|
{formatValue(limit)}
|
|
{unit && ` ${unit}`}
|
|
</>
|
|
)}
|
|
{isUnlimited && ' (ilimitado)'}
|
|
</span>
|
|
</div>
|
|
|
|
<div className="h-2 overflow-hidden rounded-full bg-gray-200">
|
|
<div
|
|
className={`h-full rounded-full transition-all ${getColorClass()}`}
|
|
style={{ width: isUnlimited ? '0%' : `${Math.min(percent, 100)}%` }}
|
|
/>
|
|
</div>
|
|
|
|
<div className="flex items-center justify-between text-xs">
|
|
<span className="text-gray-400">
|
|
{isUnlimited ? 'Sin límite' : `${percent.toFixed(1)}% usado`}
|
|
</span>
|
|
{showOverage && overage > 0 && (
|
|
<span className="font-medium text-red-600">
|
|
+{formatValue(overage)} excedente
|
|
</span>
|
|
)}
|
|
</div>
|
|
</div>
|
|
);
|
|
};
|