Enhance SIM Management Section with new features and improved UI
- Integrated subscription and invoice fetching to display relevant billing information. - Added data usage tracking and visual representation for better user insights. - Introduced new action buttons for data top-up, plan changes, and SIM reissue. - Improved error handling and loading states for a smoother user experience. - Updated layout and styling for better responsiveness and clarity.
This commit is contained in:
parent
5ad4089d4e
commit
bccfec940f
@ -5,18 +5,47 @@ import {
|
|||||||
DevicePhoneMobileIcon,
|
DevicePhoneMobileIcon,
|
||||||
ExclamationTriangleIcon,
|
ExclamationTriangleIcon,
|
||||||
ArrowPathIcon,
|
ArrowPathIcon,
|
||||||
|
SignalIcon,
|
||||||
|
ArrowsRightLeftIcon,
|
||||||
|
ArrowPathRoundedSquareIcon,
|
||||||
|
XCircleIcon,
|
||||||
} from "@heroicons/react/24/outline";
|
} from "@heroicons/react/24/outline";
|
||||||
import { SimDetailsCard, type SimDetails } from "./SimDetailsCard";
|
|
||||||
import { SimActions } from "./SimActions";
|
|
||||||
import { apiClient } from "@/lib/api";
|
import { apiClient } from "@/lib/api";
|
||||||
import { SimFeatureToggles } from "./SimFeatureToggles";
|
import { useSubscription, useSubscriptionInvoices } from "@/features/subscriptions/hooks";
|
||||||
|
import { useCreateInvoiceSsoLink } from "@/features/billing/hooks/useBilling";
|
||||||
|
import { format } from "date-fns";
|
||||||
|
import { Formatting } from "@customer-portal/domain/toolkit";
|
||||||
|
|
||||||
|
const { formatCurrency } = Formatting;
|
||||||
|
|
||||||
interface SimManagementSectionProps {
|
interface SimManagementSectionProps {
|
||||||
subscriptionId: number;
|
subscriptionId: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface SimDetails {
|
||||||
|
account: string;
|
||||||
|
msisdn: string;
|
||||||
|
iccid?: string;
|
||||||
|
imsi?: string;
|
||||||
|
eid?: string;
|
||||||
|
planCode: string;
|
||||||
|
status: "active" | "suspended" | "cancelled" | "pending";
|
||||||
|
simType: "physical" | "esim" | "standard" | "nano" | "micro";
|
||||||
|
remainingQuotaMb: number;
|
||||||
|
remainingQuotaKb: number;
|
||||||
|
voiceMailEnabled?: boolean;
|
||||||
|
callWaitingEnabled?: boolean;
|
||||||
|
internationalRoamingEnabled?: boolean;
|
||||||
|
networkType?: string;
|
||||||
|
}
|
||||||
|
|
||||||
interface SimInfo {
|
interface SimInfo {
|
||||||
details: SimDetails;
|
details: SimDetails;
|
||||||
|
usage?: {
|
||||||
|
todayUsageMb: number;
|
||||||
|
monthlyUsageMb?: number;
|
||||||
|
recentDaysUsage?: Array<{ date: string; usageMb: number }>;
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export function SimManagementSection({ subscriptionId }: SimManagementSectionProps) {
|
export function SimManagementSection({ subscriptionId }: SimManagementSectionProps) {
|
||||||
@ -24,6 +53,22 @@ export function SimManagementSection({ subscriptionId }: SimManagementSectionPro
|
|||||||
const [loading, setLoading] = useState(true);
|
const [loading, setLoading] = useState(true);
|
||||||
const [error, setError] = useState<string | null>(null);
|
const [error, setError] = useState<string | null>(null);
|
||||||
|
|
||||||
|
// Fetch subscription data
|
||||||
|
const { data: subscription } = useSubscription(subscriptionId);
|
||||||
|
|
||||||
|
// Fetch latest invoice (limit 1)
|
||||||
|
const { data: invoicesData } = useSubscriptionInvoices(subscriptionId, { limit: 1 });
|
||||||
|
const latestInvoice = invoicesData?.invoices?.[0];
|
||||||
|
|
||||||
|
// SSO link mutation for payment
|
||||||
|
const createSsoLink = useCreateInvoiceSsoLink({
|
||||||
|
onSuccess: (data) => {
|
||||||
|
if (data.url) {
|
||||||
|
window.open(data.url, '_blank');
|
||||||
|
}
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
const fetchSimInfo = useCallback(async () => {
|
const fetchSimInfo = useCallback(async () => {
|
||||||
try {
|
try {
|
||||||
setError(null);
|
setError(null);
|
||||||
@ -38,8 +83,10 @@ export function SimManagementSection({ subscriptionId }: SimManagementSectionPro
|
|||||||
throw new Error("Failed to load SIM information");
|
throw new Error("Failed to load SIM information");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Only use the details part, ignore usage data
|
setSimInfo({
|
||||||
setSimInfo({ details: payload.details });
|
details: payload.details,
|
||||||
|
usage: payload.usage
|
||||||
|
});
|
||||||
} catch (err: unknown) {
|
} catch (err: unknown) {
|
||||||
const hasStatus = (v: unknown): v is { status: number } =>
|
const hasStatus = (v: unknown): v is { status: number } =>
|
||||||
typeof v === "object" &&
|
typeof v === "object" &&
|
||||||
@ -47,7 +94,6 @@ export function SimManagementSection({ subscriptionId }: SimManagementSectionPro
|
|||||||
"status" in v &&
|
"status" in v &&
|
||||||
typeof (v as { status: unknown }).status === "number";
|
typeof (v as { status: unknown }).status === "number";
|
||||||
if (hasStatus(err) && err.status === 400) {
|
if (hasStatus(err) && err.status === 400) {
|
||||||
// Not a SIM subscription - this component shouldn't be shown
|
|
||||||
setError("This subscription is not a SIM service");
|
setError("This subscription is not a SIM service");
|
||||||
} else {
|
} else {
|
||||||
setError(err instanceof Error ? err.message : "Failed to load SIM information");
|
setError(err instanceof Error ? err.message : "Failed to load SIM information");
|
||||||
@ -67,31 +113,31 @@ export function SimManagementSection({ subscriptionId }: SimManagementSectionPro
|
|||||||
};
|
};
|
||||||
|
|
||||||
const handleActionSuccess = () => {
|
const handleActionSuccess = () => {
|
||||||
// Refresh SIM info after any successful action
|
|
||||||
void fetchSimInfo();
|
void fetchSimInfo();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handlePayInvoice = () => {
|
||||||
|
if (latestInvoice?.id) {
|
||||||
|
createSsoLink.mutate({ invoiceId: latestInvoice.id, target: 'pay' });
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const formatDate = (dateString: string | undefined) => {
|
||||||
|
if (!dateString) return "N/A";
|
||||||
|
try {
|
||||||
|
return format(new Date(dateString), "MMM d yyyy");
|
||||||
|
} catch {
|
||||||
|
return "Invalid date";
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
if (loading) {
|
if (loading) {
|
||||||
return (
|
return (
|
||||||
<div className="space-y-8">
|
<div className="space-y-6">
|
||||||
<div className="bg-white shadow-lg rounded-xl border border-gray-100 p-8">
|
<div className="bg-white/90 backdrop-blur-sm shadow-sm rounded-lg border border-gray-200 p-8">
|
||||||
<div className="flex items-center mb-6">
|
|
||||||
<div className="bg-blue-50 rounded-xl p-2 mr-4">
|
|
||||||
<DevicePhoneMobileIcon className="h-6 w-6 text-blue-600" />
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<h2 className="text-2xl font-bold text-gray-900">SIM Management</h2>
|
|
||||||
<p className="text-gray-600 mt-1">Loading your SIM service details...</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className="animate-pulse space-y-6">
|
<div className="animate-pulse space-y-6">
|
||||||
<div className="h-6 bg-gradient-to-r from-gray-200 to-gray-300 rounded-lg w-3/4"></div>
|
<div className="h-6 bg-gradient-to-r from-gray-200 to-gray-300 rounded-lg w-3/4"></div>
|
||||||
<div className="h-5 bg-gradient-to-r from-gray-200 to-gray-300 rounded-lg w-1/2"></div>
|
|
||||||
<div className="h-48 bg-gradient-to-r from-gray-200 to-gray-300 rounded-xl"></div>
|
<div className="h-48 bg-gradient-to-r from-gray-200 to-gray-300 rounded-xl"></div>
|
||||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
|
|
||||||
<div className="h-32 bg-gradient-to-r from-gray-200 to-gray-300 rounded-xl"></div>
|
|
||||||
<div className="h-32 bg-gradient-to-r from-gray-200 to-gray-300 rounded-xl"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -100,16 +146,7 @@ export function SimManagementSection({ subscriptionId }: SimManagementSectionPro
|
|||||||
|
|
||||||
if (error) {
|
if (error) {
|
||||||
return (
|
return (
|
||||||
<div className="bg-white shadow-lg rounded-xl border border-red-100 p-8">
|
<div className="bg-white/90 backdrop-blur-sm shadow-sm rounded-lg border border-red-200 p-8">
|
||||||
<div className="flex items-center mb-6">
|
|
||||||
<div className="bg-blue-50 rounded-xl p-2 mr-4">
|
|
||||||
<DevicePhoneMobileIcon className="h-6 w-6 text-blue-600" />
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<h2 className="text-2xl font-bold text-gray-900">SIM Management</h2>
|
|
||||||
<p className="text-gray-600 mt-1">Unable to load SIM information</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className="text-center py-12">
|
<div className="text-center py-12">
|
||||||
<div className="bg-red-50 rounded-full p-4 w-20 h-20 mx-auto mb-6">
|
<div className="bg-red-50 rounded-full p-4 w-20 h-20 mx-auto mb-6">
|
||||||
<ExclamationTriangleIcon className="h-12 w-12 text-red-500 mx-auto" />
|
<ExclamationTriangleIcon className="h-12 w-12 text-red-500 mx-auto" />
|
||||||
@ -120,7 +157,7 @@ export function SimManagementSection({ subscriptionId }: SimManagementSectionPro
|
|||||||
<p className="text-gray-600 mb-8 max-w-md mx-auto">{error}</p>
|
<p className="text-gray-600 mb-8 max-w-md mx-auto">{error}</p>
|
||||||
<button
|
<button
|
||||||
onClick={handleRefresh}
|
onClick={handleRefresh}
|
||||||
className="inline-flex items-center px-6 py-3 border border-transparent text-sm font-semibold rounded-xl text-white bg-gradient-to-r from-blue-600 to-blue-700 hover:from-blue-700 hover:to-blue-800 hover:shadow-lg hover:scale-105 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 transition-all duration-200"
|
className="inline-flex items-center px-6 py-3 border border-transparent text-sm font-semibold rounded-xl text-white bg-blue-600 hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 transition-all duration-200"
|
||||||
>
|
>
|
||||||
<ArrowPathIcon className="h-5 w-5 mr-2" />
|
<ArrowPathIcon className="h-5 w-5 mr-2" />
|
||||||
Retry
|
Retry
|
||||||
@ -134,99 +171,256 @@ export function SimManagementSection({ subscriptionId }: SimManagementSectionPro
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const remainingGB = (simInfo.details.remainingQuotaMb / 1000).toFixed(1);
|
||||||
|
const usedGB = simInfo.usage?.monthlyUsageMb
|
||||||
|
? (simInfo.usage.monthlyUsageMb / 1000).toFixed(2)
|
||||||
|
: simInfo.usage?.todayUsageMb
|
||||||
|
? (simInfo.usage.todayUsageMb / 1000).toFixed(2)
|
||||||
|
: "0.00";
|
||||||
|
|
||||||
|
// Calculate percentage for circle
|
||||||
|
const totalGB = parseFloat(remainingGB) + parseFloat(usedGB);
|
||||||
|
const usagePercentage = totalGB > 0 ? (parseFloat(usedGB) / totalGB) * 100 : 0;
|
||||||
|
const circumference = 2 * Math.PI * 88;
|
||||||
|
const strokeDashoffset = circumference - (usagePercentage / 100) * circumference;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div id="sim-management" className="space-y-6">
|
<div id="sim-management" className="space-y-6">
|
||||||
{/* Header Section */}
|
{/* Main Content Card */}
|
||||||
<div className="bg-white/90 backdrop-blur-sm shadow-sm rounded-lg border border-gray-200 p-6">
|
<div className="bg-white/90 backdrop-blur-sm shadow-sm rounded-lg border border-gray-200 p-6">
|
||||||
<div className="flex items-center justify-between">
|
{/* Top Section with Details and Data Circle */}
|
||||||
<div>
|
<div className="grid grid-cols-1 lg:grid-cols-2 gap-8 mb-8">
|
||||||
<h1 className="text-2xl font-bold text-gray-900">
|
{/* Left: Subscription Details */}
|
||||||
{simInfo.details.simType === "esim" ? "eSIM" : "Physical SIM"} Service
|
<div className="space-y-4">
|
||||||
</h1>
|
<div>
|
||||||
<p className="text-gray-600 mt-1">Subscription ID {subscriptionId}</p>
|
<p className="text-sm text-gray-600">
|
||||||
</div>
|
Monthly Cost: <span className="font-semibold text-gray-900">
|
||||||
<span className="inline-flex items-center px-3 py-1 rounded-full text-sm font-medium bg-green-100 text-green-800">
|
{subscription ? formatCurrency(subscription.amount) : "Loading..."}
|
||||||
{simInfo.details.status.charAt(0).toUpperCase() + simInfo.details.status.slice(1)}
|
</span>
|
||||||
</span>
|
</p>
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Two Column Layout */}
|
|
||||||
<div className="grid grid-cols-1 lg:grid-cols-3 gap-6">
|
|
||||||
{/* Left Column - Main Actions */}
|
|
||||||
<div className="lg:col-span-2 space-y-6">
|
|
||||||
{/* Subscription Details Card */}
|
|
||||||
<div className="bg-white/90 backdrop-blur-sm shadow-sm rounded-lg border border-gray-200 p-6">
|
|
||||||
<div className="mb-4">
|
|
||||||
<h2 className="text-lg font-semibold text-gray-900">Subscription Details</h2>
|
|
||||||
</div>
|
</div>
|
||||||
<div className="grid grid-cols-1 md:grid-cols-3 gap-4">
|
<div>
|
||||||
<div className="text-center">
|
<p className="text-sm text-gray-600">
|
||||||
<p className="text-sm text-gray-500 uppercase tracking-wide">Monthly Cost</p>
|
Next Billing: <span className="font-semibold text-gray-900">
|
||||||
<p className="text-lg font-semibold text-gray-900">¥3,100</p>
|
{subscription?.nextDue ? formatDate(subscription.nextDue) : "N/A"}
|
||||||
</div>
|
</span>
|
||||||
<div className="text-center">
|
</p>
|
||||||
<p className="text-sm text-gray-500 uppercase tracking-wide">Next Billing</p>
|
</div>
|
||||||
<p className="text-lg font-semibold text-gray-900">Jul 1, 2024</p>
|
<div>
|
||||||
</div>
|
<p className="text-sm text-gray-600">
|
||||||
<div className="text-center">
|
Registered: <span className="font-semibold text-gray-900">
|
||||||
<p className="text-sm text-gray-500 uppercase tracking-wide">Registration</p>
|
{subscription?.registrationDate ? formatDate(subscription.registrationDate) : "N/A"}
|
||||||
<p className="text-lg font-semibold text-gray-900">Aug 2, 2023</p>
|
</span>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Latest Invoice Section - Mobile View */}
|
||||||
|
{latestInvoice && (
|
||||||
|
<div className="lg:hidden mt-6 p-4 bg-gray-50 rounded-lg border border-gray-200">
|
||||||
|
<p className="text-xs text-gray-600 mb-1">Latest Invoice</p>
|
||||||
|
<p className="text-2xl font-bold text-gray-900 mb-3">
|
||||||
|
{formatCurrency(latestInvoice.total)}
|
||||||
|
</p>
|
||||||
|
<button
|
||||||
|
onClick={handlePayInvoice}
|
||||||
|
disabled={createSsoLink.isPending || latestInvoice.status === 'paid'}
|
||||||
|
className="w-full px-4 py-2 bg-blue-600 text-white text-sm font-semibold rounded-lg hover:bg-blue-700 disabled:bg-gray-400 disabled:cursor-not-allowed transition-colors"
|
||||||
|
>
|
||||||
|
{latestInvoice.status === 'paid' ? 'PAID' : createSsoLink.isPending ? 'Loading...' : 'PAY'}
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{/* Top Up Data Button */}
|
||||||
|
<div className="pt-4">
|
||||||
|
<button
|
||||||
|
onClick={() => {/* TODO: Open top-up modal */}}
|
||||||
|
className="w-full px-6 py-3 bg-blue-600 text-white font-semibold rounded-lg hover:bg-blue-700 transition-colors"
|
||||||
|
>
|
||||||
|
Top Up Data
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* SIM Management Actions Card */}
|
{/* Right: Data Usage Circle */}
|
||||||
<div className="bg-white/90 backdrop-blur-sm shadow-sm rounded-lg border border-gray-200 p-6">
|
<div className="flex items-center justify-center">
|
||||||
<div className="mb-4">
|
<div className="relative w-48 h-48">
|
||||||
<h2 className="text-lg font-semibold text-gray-900">SIM Management Actions</h2>
|
<svg className="w-full h-full transform -rotate-90">
|
||||||
|
<circle
|
||||||
|
cx="96"
|
||||||
|
cy="96"
|
||||||
|
r="88"
|
||||||
|
fill="none"
|
||||||
|
stroke="#e5e7eb"
|
||||||
|
strokeWidth="12"
|
||||||
|
/>
|
||||||
|
<circle
|
||||||
|
cx="96"
|
||||||
|
cy="96"
|
||||||
|
r="88"
|
||||||
|
fill="none"
|
||||||
|
stroke="#3b82f6"
|
||||||
|
strokeWidth="12"
|
||||||
|
strokeLinecap="round"
|
||||||
|
strokeDasharray={circumference}
|
||||||
|
strokeDashoffset={strokeDashoffset}
|
||||||
|
className="transition-all duration-500"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
<div className="absolute inset-0 flex flex-col items-center justify-center">
|
||||||
|
<p className="text-xs text-gray-600">Remaining data</p>
|
||||||
|
<p className="text-4xl font-bold text-gray-900">{remainingGB} GB</p>
|
||||||
|
<p className="text-sm text-blue-600">-{usedGB} GB</p>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<SimActions
|
|
||||||
subscriptionId={subscriptionId}
|
|
||||||
simType={simInfo.details.simType}
|
|
||||||
status={simInfo.details.status}
|
|
||||||
currentPlanCode={simInfo.details.planCode}
|
|
||||||
onTopUpSuccess={handleActionSuccess}
|
|
||||||
onPlanChangeSuccess={handleActionSuccess}
|
|
||||||
onCancelSuccess={handleActionSuccess}
|
|
||||||
onReissueSuccess={handleActionSuccess}
|
|
||||||
embedded={true}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Voice Status Card */}
|
|
||||||
<div className="bg-white/90 backdrop-blur-sm shadow-sm rounded-lg border border-gray-200 p-6">
|
|
||||||
<div className="mb-4">
|
|
||||||
<h2 className="text-lg font-semibold text-gray-900">Voice Status</h2>
|
|
||||||
</div>
|
|
||||||
<SimFeatureToggles
|
|
||||||
subscriptionId={subscriptionId}
|
|
||||||
voiceMailEnabled={simInfo.details.voiceMailEnabled}
|
|
||||||
callWaitingEnabled={simInfo.details.callWaitingEnabled}
|
|
||||||
internationalRoamingEnabled={simInfo.details.internationalRoamingEnabled}
|
|
||||||
networkType={simInfo.details.networkType}
|
|
||||||
onChanged={handleActionSuccess}
|
|
||||||
embedded
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Right Column - SIM Details & Usage */}
|
{/* SIM Management Actions - 2x2 Grid on Desktop, 2x2 on Mobile */}
|
||||||
<div className="space-y-6">
|
<div className="mb-8">
|
||||||
{/* SIM Details Card */}
|
<h3 className="text-lg font-semibold text-gray-900 mb-4">SIM Management Actions</h3>
|
||||||
<div className="bg-white/90 backdrop-blur-sm shadow-sm rounded-lg border border-gray-200 p-6">
|
<div className="grid grid-cols-2 gap-4">
|
||||||
<div className="mb-4">
|
<button
|
||||||
<h2 className="text-lg font-semibold text-gray-900">SIM Details</h2>
|
onClick={() => {/* TODO: Open top-up modal */}}
|
||||||
</div>
|
className="flex flex-col items-center justify-center p-6 bg-white border-2 border-gray-200 rounded-lg hover:border-blue-500 hover:shadow-md transition-all duration-200"
|
||||||
<SimDetailsCard
|
>
|
||||||
simDetails={simInfo.details}
|
<SignalIcon className="h-8 w-8 text-gray-700 mb-2" />
|
||||||
isLoading={false}
|
<span className="text-sm font-medium text-gray-900">Data Top up</span>
|
||||||
error={null}
|
</button>
|
||||||
embedded={true}
|
|
||||||
showFeaturesSummary={false}
|
<button
|
||||||
/>
|
onClick={() => {/* TODO: Open change plan modal */}}
|
||||||
|
className="flex flex-col items-center justify-center p-6 bg-white border-2 border-gray-200 rounded-lg hover:border-blue-500 hover:shadow-md transition-all duration-200"
|
||||||
|
>
|
||||||
|
<ArrowsRightLeftIcon className="h-8 w-8 text-gray-700 mb-2" />
|
||||||
|
<span className="text-sm font-medium text-gray-900">Change Plan</span>
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<button
|
||||||
|
onClick={() => {/* TODO: Open reissue modal */}}
|
||||||
|
disabled={simInfo.details.simType !== 'esim'}
|
||||||
|
className="flex flex-col items-center justify-center p-6 bg-white border-2 border-gray-200 rounded-lg hover:border-blue-500 hover:shadow-md transition-all duration-200 disabled:opacity-50 disabled:cursor-not-allowed"
|
||||||
|
>
|
||||||
|
<ArrowPathRoundedSquareIcon className="h-8 w-8 text-gray-700 mb-2" />
|
||||||
|
<span className="text-sm font-medium text-gray-900">Reissue SIM</span>
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<button
|
||||||
|
onClick={() => {/* TODO: Open cancel modal */}}
|
||||||
|
className="flex flex-col items-center justify-center p-6 bg-white border-2 border-gray-200 rounded-lg hover:border-red-500 hover:shadow-md transition-all duration-200"
|
||||||
|
>
|
||||||
|
<XCircleIcon className="h-8 w-8 text-gray-700 mb-2" />
|
||||||
|
<span className="text-sm font-medium text-gray-900">Cancel SIM</span>
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{/* Voice Status Section */}
|
||||||
|
<div className="mb-8">
|
||||||
|
<h3 className="text-lg font-semibold text-gray-900 mb-4">Voice Status</h3>
|
||||||
|
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||||
|
<div className="p-4 bg-white border border-gray-200 rounded-lg">
|
||||||
|
<div className="flex items-center justify-between">
|
||||||
|
<div>
|
||||||
|
<p className="font-medium text-gray-900">Voice Mail</p>
|
||||||
|
{simInfo.details.voiceMailEnabled && (
|
||||||
|
<p className="text-xs text-blue-600">
|
||||||
|
On
|
||||||
|
<br />
|
||||||
|
<span className="text-blue-600">Enabled</span>
|
||||||
|
</p>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
<label className="relative inline-flex items-center cursor-pointer">
|
||||||
|
<input
|
||||||
|
type="checkbox"
|
||||||
|
checked={simInfo.details.voiceMailEnabled || false}
|
||||||
|
className="sr-only peer"
|
||||||
|
readOnly
|
||||||
|
/>
|
||||||
|
<div className="w-11 h-6 bg-gray-200 peer-focus:outline-none peer-focus:ring-4 peer-focus:ring-blue-300 rounded-full peer peer-checked:after:translate-x-full peer-checked:after:border-white after:content-[''] after:absolute after:top-[2px] after:left-[2px] after:bg-white after:border-gray-300 after:border after:rounded-full after:h-5 after:w-5 after:transition-all peer-checked:bg-blue-600"></div>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="p-4 bg-white border border-gray-200 rounded-lg">
|
||||||
|
<div className="flex items-center justify-between">
|
||||||
|
<div>
|
||||||
|
<p className="font-medium text-gray-900">Network Type</p>
|
||||||
|
<p className="text-xs text-gray-600">
|
||||||
|
{simInfo.details.networkType ? simInfo.details.networkType : 'Disabled'}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<label className="relative inline-flex items-center cursor-pointer">
|
||||||
|
<input
|
||||||
|
type="checkbox"
|
||||||
|
checked={!!simInfo.details.networkType}
|
||||||
|
className="sr-only peer"
|
||||||
|
readOnly
|
||||||
|
/>
|
||||||
|
<div className="w-11 h-6 bg-gray-200 peer-focus:outline-none peer-focus:ring-4 peer-focus:ring-blue-300 rounded-full peer peer-checked:after:translate-x-full peer-checked:after:border-white after:content-[''] after:absolute after:top-[2px] after:left-[2px] after:bg-white after:border-gray-300 after:border after:rounded-full after:h-5 after:w-5 after:transition-all peer-checked:bg-blue-600"></div>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="p-4 bg-white border border-gray-200 rounded-lg">
|
||||||
|
<div className="flex items-center justify-between">
|
||||||
|
<div>
|
||||||
|
<p className="font-medium text-gray-900">Call Waiting</p>
|
||||||
|
</div>
|
||||||
|
<label className="relative inline-flex items-center cursor-pointer">
|
||||||
|
<input
|
||||||
|
type="checkbox"
|
||||||
|
checked={simInfo.details.callWaitingEnabled || false}
|
||||||
|
className="sr-only peer"
|
||||||
|
readOnly
|
||||||
|
/>
|
||||||
|
<div className="w-11 h-6 bg-gray-200 peer-focus:outline-none peer-focus:ring-4 peer-focus:ring-blue-300 rounded-full peer peer-checked:after:translate-x-full peer-checked:after:border-white after:content-[''] after:absolute after:top-[2px] after:left-[2px] after:bg-white after:border-gray-300 after:border after:rounded-full after:h-5 after:w-5 after:transition-all peer-checked:bg-blue-600"></div>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="p-4 bg-white border border-gray-200 rounded-lg">
|
||||||
|
<div className="flex items-center justify-between">
|
||||||
|
<div>
|
||||||
|
<p className="font-medium text-gray-900">International Roaming</p>
|
||||||
|
</div>
|
||||||
|
<label className="relative inline-flex items-center cursor-pointer">
|
||||||
|
<input
|
||||||
|
type="checkbox"
|
||||||
|
checked={simInfo.details.internationalRoamingEnabled || false}
|
||||||
|
className="sr-only peer"
|
||||||
|
readOnly
|
||||||
|
/>
|
||||||
|
<div className="w-11 h-6 bg-gray-200 peer-focus:outline-none peer-focus:ring-4 peer-focus:ring-blue-300 rounded-full peer peer-checked:after:translate-x-full peer-checked:after:border-white after:content-[''] after:absolute after:top-[2px] after:left-[2px] after:bg-white after:border-gray-300 after:border after:rounded-full after:h-5 after:w-5 after:transition-all peer-checked:bg-blue-600"></div>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Important Notes */}
|
||||||
|
<div className="bg-gray-50 border border-gray-200 rounded-lg p-6">
|
||||||
|
<h3 className="text-lg font-semibold text-gray-900 mb-3">Important Notes</h3>
|
||||||
|
<ul className="space-y-2 text-sm text-gray-700">
|
||||||
|
<li className="flex items-start">
|
||||||
|
<span className="mr-2">•</span>
|
||||||
|
<span>Changes will take effect instantaneously (approx. 30min)</span>
|
||||||
|
</li>
|
||||||
|
<li className="flex items-start">
|
||||||
|
<span className="mr-2">•</span>
|
||||||
|
<span>May require smartphone device restart after changes are applied</span>
|
||||||
|
</li>
|
||||||
|
<li className="flex items-start">
|
||||||
|
<span className="mr-2">•</span>
|
||||||
|
<span>Voice, network, and plan changes must be requested at least 30 minutes apart.</span>
|
||||||
|
</li>
|
||||||
|
<li className="flex items-start">
|
||||||
|
<span className="mr-2">•</span>
|
||||||
|
<span>Changes to Voice Mail / Call Waiting must be requested before the 25th of the month</span>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user