258 lines
9.3 KiB
TypeScript
Raw Normal View History

"use client";
import React from "react";
import { ChartBarIcon, ExclamationTriangleIcon } from "@heroicons/react/24/outline";
export interface SimUsage {
account: string;
todayUsageKb: number;
todayUsageMb: number;
recentDaysUsage: Array<{
date: string;
usageKb: number;
usageMb: number;
}>;
isBlacklisted: boolean;
}
interface DataUsageChartProps {
usage: SimUsage;
remainingQuotaMb: number;
isLoading?: boolean;
error?: string | null;
embedded?: boolean; // when true, render content without card container
}
export function DataUsageChart({
usage,
remainingQuotaMb,
isLoading,
error,
embedded = false,
}: DataUsageChartProps) {
const formatUsage = (usageMb: number) => {
if (usageMb >= 1000) {
return `${(usageMb / 1000).toFixed(1)} GB`;
}
return `${usageMb.toFixed(0)} MB`;
};
const getUsageColor = (percentage: number) => {
if (percentage >= 90) return "bg-red-500";
if (percentage >= 75) return "bg-yellow-500";
if (percentage >= 50) return "bg-orange-500";
return "bg-green-500";
};
const getUsageTextColor = (percentage: number) => {
if (percentage >= 90) return "text-red-600";
if (percentage >= 75) return "text-yellow-600";
if (percentage >= 50) return "text-orange-600";
return "text-green-600";
};
if (isLoading) {
return (
<div className={`${embedded ? "" : "bg-white shadow rounded-lg "}p-6`}>
<div className="animate-pulse">
<div className="h-6 bg-gray-200 rounded w-1/3 mb-4"></div>
<div className="h-4 bg-gray-200 rounded w-full mb-2"></div>
<div className="h-8 bg-gray-200 rounded mb-4"></div>
<div className="space-y-2">
<div className="h-4 bg-gray-200 rounded w-3/4"></div>
<div className="h-4 bg-gray-200 rounded w-1/2"></div>
</div>
</div>
</div>
);
}
if (error) {
return (
<div className={`${embedded ? "" : "bg-white shadow rounded-lg "}p-6`}>
<div className="text-center">
<ExclamationTriangleIcon className="h-12 w-12 text-red-400 mx-auto mb-4" />
<h3 className="text-lg font-medium text-gray-900 mb-2">Error Loading Usage Data</h3>
<p className="text-red-600">{error}</p>
</div>
</div>
);
}
// Calculate total usage from recent days (assume it includes today)
const totalRecentUsage =
usage.recentDaysUsage.reduce((sum, day) => sum + day.usageMb, 0) + usage.todayUsageMb;
const totalQuota = remainingQuotaMb + totalRecentUsage;
const usagePercentage = totalQuota > 0 ? (totalRecentUsage / totalQuota) * 100 : 0;
return (
<div
className={`${embedded ? "" : "bg-white shadow-lg rounded-xl border border-gray-100 hover:shadow-xl transition-shadow duration-300"}`}
>
{/* Header */}
<div className={`${embedded ? "" : "px-6 lg:px-8 py-5 border-b border-gray-200"}`}>
<div className="flex items-center">
<div className="bg-blue-50 rounded-xl p-2 mr-4">
<ChartBarIcon className="h-6 w-6 text-blue-600" />
</div>
<div>
<h3 className="text-xl font-semibold text-gray-900">Data Usage</h3>
<p className="text-sm text-gray-600">Current month usage and remaining quota</p>
</div>
</div>
</div>
{/* Content */}
<div className={`${embedded ? "" : "px-6 lg:px-8 py-6"}`}>
{/* Current Usage Overview */}
<div className="mb-6">
<div className="flex justify-between items-center mb-2">
<span className="text-sm font-medium text-gray-700">Used this month</span>
<span className={`text-sm font-semibold ${getUsageTextColor(usagePercentage)}`}>
{formatUsage(totalRecentUsage)} of {formatUsage(totalQuota)}
</span>
</div>
{/* Progress Bar */}
<div className="w-full bg-gray-200 rounded-full h-3">
<div
className={`h-3 rounded-full transition-all duration-300 ${getUsageColor(usagePercentage)}`}
style={{ width: `${Math.min(usagePercentage, 100)}%` }}
></div>
</div>
<div className="flex justify-between text-xs text-gray-500 mt-1">
<span>0%</span>
<span className={getUsageTextColor(usagePercentage)}>
{usagePercentage.toFixed(1)}% used
</span>
<span>100%</span>
</div>
</div>
{/* Today's Usage */}
<div className="grid grid-cols-1 sm:grid-cols-2 gap-4 mb-8">
<div className="bg-gradient-to-br from-blue-50 to-blue-100 rounded-xl p-6 border border-blue-200">
<div className="flex items-center justify-between">
<div>
<div className="text-3xl font-bold text-blue-600">
{formatUsage(usage.todayUsageMb)}
</div>
<div className="text-sm font-medium text-blue-700 mt-1">Used today</div>
</div>
<div className="bg-blue-200 rounded-full p-3">
<svg
className="h-6 w-6 text-blue-600"
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
>
<path
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth={2}
d="M13 7h8m0 0v8m0-8l-8 8-4-4-6 6"
/>
</svg>
</div>
</div>
</div>
<div className="bg-gradient-to-br from-green-50 to-green-100 rounded-xl p-6 border border-green-200">
<div className="flex items-center justify-between">
<div>
<div className="text-3xl font-bold text-green-600">
{formatUsage(remainingQuotaMb)}
</div>
<div className="text-sm font-medium text-green-700 mt-1">Remaining</div>
</div>
<div className="bg-green-200 rounded-full p-3">
<svg
className="h-6 w-6 text-green-600"
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
>
<path
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth={2}
d="M20 12H4m16 0l-4 4m4-4l-4-4"
/>
</svg>
</div>
</div>
</div>
</div>
{/* Recent Days Usage */}
{usage.recentDaysUsage.length > 0 && (
<div>
<h4 className="text-sm font-medium text-gray-500 uppercase tracking-wider mb-3">
Recent Usage History
</h4>
<div className="space-y-2">
{usage.recentDaysUsage.slice(0, 5).map((day, index) => {
const dayPercentage = totalQuota > 0 ? (day.usageMb / totalQuota) * 100 : 0;
return (
<div key={index} className="flex items-center justify-between py-2">
<span className="text-sm text-gray-600">
{new Date(day.date).toLocaleDateString("en-US", {
month: "short",
day: "numeric",
})}
</span>
<div className="flex items-center space-x-3">
<div className="w-24 bg-gray-200 rounded-full h-2">
<div
className="bg-blue-500 h-2 rounded-full transition-all duration-300"
style={{ width: `${Math.min(dayPercentage, 100)}%` }}
></div>
</div>
<span className="text-sm font-medium text-gray-900 w-16 text-right">
{formatUsage(day.usageMb)}
</span>
</div>
</div>
);
})}
</div>
</div>
)}
{/* Warnings */}
{usagePercentage >= 90 && (
<div className="mt-6 bg-red-50 border border-red-200 rounded-lg p-4">
<div className="flex items-center">
<ExclamationTriangleIcon className="h-5 w-5 text-red-500 mr-2" />
<div>
<h4 className="text-sm font-medium text-red-800">High Usage Warning</h4>
<p className="text-sm text-red-700 mt-1">
You have used {usagePercentage.toFixed(1)}% of your data quota. Consider topping
up to avoid service interruption.
</p>
</div>
</div>
</div>
)}
{usagePercentage >= 75 && usagePercentage < 90 && (
<div className="mt-6 bg-yellow-50 border border-yellow-200 rounded-lg p-4">
<div className="flex items-center">
<ExclamationTriangleIcon className="h-5 w-5 text-yellow-500 mr-2" />
<div>
<h4 className="text-sm font-medium text-yellow-800">Usage Notice</h4>
<p className="text-sm text-yellow-700 mt-1">
You have used {usagePercentage.toFixed(1)}% of your data quota. Consider
monitoring your usage.
</p>
</div>
</div>
</div>
)}
</div>
</div>
);
}