326 lines
12 KiB
TypeScript
Raw Normal View History

"use client";
import React from 'react';
import {
DevicePhoneMobileIcon,
WifiIcon,
SignalIcon,
ClockIcon,
CheckCircleIcon,
ExclamationTriangleIcon,
XCircleIcon
} from '@heroicons/react/24/outline';
export interface SimDetails {
account: string;
msisdn: string;
iccid?: string;
imsi?: string;
eid?: string;
planCode: string;
status: 'active' | 'suspended' | 'cancelled' | 'pending';
simType: 'physical' | 'esim';
size: 'standard' | 'nano' | 'micro' | 'esim';
hasVoice: boolean;
hasSms: boolean;
remainingQuotaKb: number;
remainingQuotaMb: number;
startDate?: string;
ipv4?: string;
ipv6?: string;
voiceMailEnabled?: boolean;
callWaitingEnabled?: boolean;
internationalRoamingEnabled?: boolean;
networkType?: string;
pendingOperations?: Array<{
operation: string;
scheduledDate: string;
}>;
}
interface SimDetailsCardProps {
simDetails: SimDetails;
isLoading?: boolean;
error?: string | null;
}
export function SimDetailsCard({ simDetails, isLoading, error }: SimDetailsCardProps) {
const getStatusIcon = (status: string) => {
switch (status) {
case 'active':
return <CheckCircleIcon className="h-6 w-6 text-green-500" />;
case 'suspended':
return <ExclamationTriangleIcon className="h-6 w-6 text-yellow-500" />;
case 'cancelled':
return <XCircleIcon className="h-6 w-6 text-red-500" />;
case 'pending':
return <ClockIcon className="h-6 w-6 text-blue-500" />;
default:
return <DevicePhoneMobileIcon className="h-6 w-6 text-gray-500" />;
}
};
const getStatusColor = (status: string) => {
switch (status) {
case 'active':
return 'bg-green-100 text-green-800';
case 'suspended':
return 'bg-yellow-100 text-yellow-800';
case 'cancelled':
return 'bg-red-100 text-red-800';
case 'pending':
return 'bg-blue-100 text-blue-800';
default:
return 'bg-gray-100 text-gray-800';
}
};
const formatDate = (dateString: string) => {
try {
const date = new Date(dateString);
return date.toLocaleDateString('en-US', {
year: 'numeric',
month: 'short',
day: 'numeric',
});
} catch {
return dateString;
}
};
const formatQuota = (quotaMb: number) => {
if (quotaMb >= 1024) {
return `${(quotaMb / 1024).toFixed(1)} GB`;
}
return `${quotaMb.toFixed(0)} MB`;
};
if (isLoading) {
return (
<div className="bg-white shadow rounded-lg p-6">
<div className="animate-pulse">
<div className="flex items-center space-x-4">
<div className="rounded-full bg-gray-200 h-12 w-12"></div>
<div className="flex-1 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 className="mt-6 space-y-3">
<div className="h-4 bg-gray-200 rounded"></div>
<div className="h-4 bg-gray-200 rounded w-5/6"></div>
<div className="h-4 bg-gray-200 rounded w-4/6"></div>
</div>
</div>
</div>
);
}
if (error) {
return (
<div className="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 SIM Details</h3>
<p className="text-red-600">{error}</p>
</div>
</div>
);
}
// Specialized, minimal eSIM details view
if (simDetails.simType === 'esim') {
return (
<div className="bg-white shadow rounded-lg">
<div className="px-6 py-4 border-b border-gray-200">
<div className="flex items-center justify-between">
<div className="flex items-center">
<WifiIcon className="h-8 w-8 text-blue-600 mr-3" />
<div>
<h3 className="text-lg font-medium text-gray-900">eSIM Details</h3>
<p className="text-sm text-gray-500">Current Plan: {simDetails.planCode}</p>
</div>
</div>
<span className={`inline-flex px-3 py-1 text-sm font-semibold rounded-full ${getStatusColor(simDetails.status)}`}>
{simDetails.status.charAt(0).toUpperCase() + simDetails.status.slice(1)}
</span>
</div>
</div>
<div className="px-6 py-4">
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
<div>
<h4 className="text-sm font-medium text-gray-500 uppercase tracking-wider mb-3">SIM Information</h4>
<div className="space-y-3">
<div>
<label className="text-xs text-gray-500">Phone Number</label>
<p className="text-sm font-medium text-gray-900">{simDetails.msisdn}</p>
</div>
<div>
<label className="text-xs text-gray-500">Data Remaining</label>
<p className="text-lg font-semibold text-green-600">{formatQuota(simDetails.remainingQuotaMb)}</p>
</div>
</div>
</div>
<div>
<h4 className="text-sm font-medium text-gray-500 uppercase tracking-wider mb-3">Service Features</h4>
<div className="space-y-2 text-sm">
<div className="flex justify-between"><span>Voice Mail (¥300/month)</span><span className="font-medium">{simDetails.voiceMailEnabled ? 'Enabled' : 'Disabled'}</span></div>
<div className="flex justify-between"><span>Call Waiting (¥300/month)</span><span className="font-medium">{simDetails.callWaitingEnabled ? 'Enabled' : 'Disabled'}</span></div>
<div className="flex justify-between"><span>International Roaming</span><span className="font-medium">{simDetails.internationalRoamingEnabled ? 'Enabled' : 'Disabled'}</span></div>
<div className="flex justify-between"><span>4G/5G</span><span className="font-medium">{simDetails.networkType || '5G'}</span></div>
</div>
</div>
</div>
{/* Plan quick action */}
<div className="mt-6 pt-4 border-t border-gray-200 flex items-center justify-between">
<div className="text-sm text-gray-600">Current Plan: <span className="font-medium text-gray-900">{simDetails.planCode}</span></div>
<a href="#sim-actions" className="inline-flex items-center px-3 py-2 border border-gray-300 rounded-md text-sm font-medium text-gray-700 bg-white hover:bg-gray-50">
Change Plan
</a>
</div>
</div>
</div>
);
}
return (
<div className="bg-white shadow rounded-lg">
{/* Header */}
<div className="px-6 py-4 border-b border-gray-200">
<div className="flex items-center justify-between">
<div className="flex items-center">
<div className="text-2xl mr-3">
{simDetails.simType === 'esim' ? <WifiIcon className="h-8 w-8 text-blue-600" /> : <DevicePhoneMobileIcon className="h-8 w-8 text-blue-600" />}
</div>
<div>
<h3 className="text-lg font-medium text-gray-900">
{simDetails.simType === 'esim' ? 'eSIM Details' : 'Physical SIM Details'}
</h3>
<p className="text-sm text-gray-500">
{simDetails.planCode} {simDetails.simType === 'physical' ? `${simDetails.size} SIM` : 'eSIM'}
</p>
</div>
</div>
<div className="flex items-center space-x-3">
{getStatusIcon(simDetails.status)}
<span className={`inline-flex px-3 py-1 text-sm font-semibold rounded-full ${getStatusColor(simDetails.status)}`}>
{simDetails.status.charAt(0).toUpperCase() + simDetails.status.slice(1)}
</span>
</div>
</div>
</div>
{/* Content */}
<div className="px-6 py-4">
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
{/* SIM Information */}
<div>
<h4 className="text-sm font-medium text-gray-500 uppercase tracking-wider mb-3">
SIM Information
</h4>
<div className="space-y-3">
<div>
<label className="text-xs text-gray-500">Phone Number</label>
<p className="text-sm font-medium text-gray-900">{simDetails.msisdn}</p>
</div>
{simDetails.simType === 'physical' && (
<div>
<label className="text-xs text-gray-500">ICCID</label>
<p className="text-sm font-mono text-gray-900 break-all">{simDetails.iccid}</p>
</div>
)}
{simDetails.eid && (
<div>
<label className="text-xs text-gray-500">EID (eSIM)</label>
<p className="text-sm font-mono text-gray-900 break-all">{simDetails.eid}</p>
</div>
)}
{simDetails.imsi && (
<div>
<label className="text-xs text-gray-500">IMSI</label>
<p className="text-sm font-mono text-gray-900">{simDetails.imsi}</p>
</div>
)}
{simDetails.startDate && (
<div>
<label className="text-xs text-gray-500">Service Start Date</label>
<p className="text-sm text-gray-900">{formatDate(simDetails.startDate)}</p>
</div>
)}
</div>
</div>
{/* Service Features */}
<div>
<h4 className="text-sm font-medium text-gray-500 uppercase tracking-wider mb-3">
Service Features
</h4>
<div className="space-y-3">
<div>
<label className="text-xs text-gray-500">Data Remaining</label>
<p className="text-lg font-semibold text-green-600">{formatQuota(simDetails.remainingQuotaMb)}</p>
</div>
<div className="flex items-center space-x-4">
<div className="flex items-center">
<SignalIcon className={`h-4 w-4 mr-1 ${simDetails.hasVoice ? 'text-green-500' : 'text-gray-400'}`} />
<span className={`text-sm ${simDetails.hasVoice ? 'text-green-600' : 'text-gray-500'}`}>
Voice {simDetails.hasVoice ? 'Enabled' : 'Disabled'}
</span>
</div>
<div className="flex items-center">
<DevicePhoneMobileIcon className={`h-4 w-4 mr-1 ${simDetails.hasSms ? 'text-green-500' : 'text-gray-400'}`} />
<span className={`text-sm ${simDetails.hasSms ? 'text-green-600' : 'text-gray-500'}`}>
SMS {simDetails.hasSms ? 'Enabled' : 'Disabled'}
</span>
</div>
</div>
{(simDetails.ipv4 || simDetails.ipv6) && (
<div>
<label className="text-xs text-gray-500">IP Address</label>
<div className="space-y-1">
{simDetails.ipv4 && (
<p className="text-sm font-mono text-gray-900">IPv4: {simDetails.ipv4}</p>
)}
{simDetails.ipv6 && (
<p className="text-sm font-mono text-gray-900">IPv6: {simDetails.ipv6}</p>
)}
</div>
</div>
)}
</div>
</div>
</div>
{/* Pending Operations */}
{simDetails.pendingOperations && simDetails.pendingOperations.length > 0 && (
<div className="mt-6 pt-6 border-t border-gray-200">
<h4 className="text-sm font-medium text-gray-500 uppercase tracking-wider mb-3">
Pending Operations
</h4>
<div className="bg-blue-50 rounded-lg p-4">
{simDetails.pendingOperations.map((operation, index) => (
<div key={index} className="flex items-center text-sm">
<ClockIcon className="h-4 w-4 text-blue-500 mr-2" />
<span className="text-blue-800">
{operation.operation} scheduled for {formatDate(operation.scheduledDate)}
</span>
</div>
))}
</div>
</div>
)}
</div>
</div>
);
}