335 lines
13 KiB
TypeScript
335 lines
13 KiB
TypeScript
"use client";
|
|
|
|
import { forwardRef } from "react";
|
|
import { format } from "date-fns";
|
|
import {
|
|
ServerIcon,
|
|
CheckCircleIcon,
|
|
ExclamationTriangleIcon,
|
|
ClockIcon,
|
|
XCircleIcon,
|
|
CalendarIcon,
|
|
CurrencyYenIcon,
|
|
IdentificationIcon,
|
|
TagIcon,
|
|
} from "@heroicons/react/24/outline";
|
|
import { StatusPill } from "@/components/atoms/status-pill";
|
|
import { SubCard } from "@/components/molecules/SubCard/SubCard";
|
|
import { formatCurrency, getCurrencyLocale } from "@customer-portal/domain";
|
|
import type { Subscription } from "@customer-portal/domain";
|
|
import { cn } from "@/lib/utils";
|
|
|
|
interface SubscriptionDetailsProps {
|
|
subscription: Subscription;
|
|
showServiceSpecificSections?: boolean;
|
|
className?: string;
|
|
}
|
|
|
|
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 "Terminated":
|
|
return <XCircleIcon className="h-6 w-6 text-red-500" />;
|
|
case "Cancelled":
|
|
return <XCircleIcon className="h-6 w-6 text-gray-500" />;
|
|
case "Pending":
|
|
return <ClockIcon className="h-6 w-6 text-blue-500" />;
|
|
default:
|
|
return <ServerIcon className="h-6 w-6 text-gray-500" />;
|
|
}
|
|
};
|
|
|
|
const getStatusVariant = (status: string) => {
|
|
switch (status) {
|
|
case "Active":
|
|
return "success" as const;
|
|
case "Suspended":
|
|
return "warning" as const;
|
|
case "Terminated":
|
|
return "error" as const;
|
|
case "Cancelled":
|
|
return "neutral" as const;
|
|
case "Pending":
|
|
return "info" as const;
|
|
default:
|
|
return "neutral" as const;
|
|
}
|
|
};
|
|
|
|
const formatDate = (dateString: string | undefined) => {
|
|
if (!dateString) return "N/A";
|
|
try {
|
|
return format(new Date(dateString), "MMM d, yyyy");
|
|
} catch {
|
|
return "Invalid date";
|
|
}
|
|
};
|
|
|
|
const formatBillingLabel = (cycle: string) => {
|
|
switch (cycle) {
|
|
case "Monthly":
|
|
return "Monthly Billing";
|
|
case "Annually":
|
|
return "Annual Billing";
|
|
case "Quarterly":
|
|
return "Quarterly Billing";
|
|
case "Semi-Annually":
|
|
return "Semi-Annual Billing";
|
|
case "Biennially":
|
|
return "Biennial Billing";
|
|
case "Triennially":
|
|
return "Triennial Billing";
|
|
default:
|
|
return "One-time Payment";
|
|
}
|
|
};
|
|
|
|
const isSimService = (productName: string) => {
|
|
return productName.toLowerCase().includes("sim");
|
|
};
|
|
|
|
const isInternetService = (productName: string) => {
|
|
const name = productName.toLowerCase();
|
|
return name.includes("internet") || name.includes("broadband") || name.includes("fiber");
|
|
};
|
|
|
|
const isVpnService = (productName: string) => {
|
|
return productName.toLowerCase().includes("vpn");
|
|
};
|
|
|
|
export const SubscriptionDetails = forwardRef<HTMLDivElement, SubscriptionDetailsProps>(
|
|
({ subscription, showServiceSpecificSections = true, className }, ref) => {
|
|
return (
|
|
<div ref={ref} className={cn("space-y-6", className)}>
|
|
{/* Main Details Card */}
|
|
<SubCard
|
|
header={
|
|
<div className="flex items-center justify-between">
|
|
<div className="flex items-center space-x-3">
|
|
{getStatusIcon(subscription.status)}
|
|
<div>
|
|
<h3 className="text-lg font-semibold text-gray-900">Subscription Details</h3>
|
|
<p className="text-sm text-gray-500">Service subscription information</p>
|
|
</div>
|
|
</div>
|
|
<StatusPill
|
|
label={subscription.status}
|
|
variant={getStatusVariant(subscription.status)}
|
|
size="lg"
|
|
/>
|
|
</div>
|
|
}
|
|
>
|
|
<div className="grid grid-cols-1 md:grid-cols-3 gap-6">
|
|
{/* Billing Amount */}
|
|
<div className="space-y-2">
|
|
<div className="flex items-center space-x-2">
|
|
<CurrencyYenIcon className="h-5 w-5 text-gray-400" />
|
|
<h4 className="text-sm font-medium text-gray-500 uppercase tracking-wider">
|
|
Billing Amount
|
|
</h4>
|
|
</div>
|
|
<p className="text-2xl font-bold text-gray-900">
|
|
{formatCurrency(subscription.amount, {
|
|
currency: "JPY",
|
|
locale: getCurrencyLocale("JPY"),
|
|
})}
|
|
</p>
|
|
<p className="text-sm text-gray-500">{formatBillingLabel(subscription.cycle)}</p>
|
|
</div>
|
|
|
|
{/* Next Due Date */}
|
|
<div className="space-y-2">
|
|
<div className="flex items-center space-x-2">
|
|
<CalendarIcon className="h-5 w-5 text-gray-400" />
|
|
<h4 className="text-sm font-medium text-gray-500 uppercase tracking-wider">
|
|
Next Due Date
|
|
</h4>
|
|
</div>
|
|
<p className="text-lg font-semibold text-gray-900">
|
|
{formatDate(subscription.nextDue)}
|
|
</p>
|
|
<p className="text-sm text-gray-500">Due date</p>
|
|
</div>
|
|
|
|
{/* Registration Date */}
|
|
<div className="space-y-2">
|
|
<div className="flex items-center space-x-2">
|
|
<TagIcon className="h-5 w-5 text-gray-400" />
|
|
<h4 className="text-sm font-medium text-gray-500 uppercase tracking-wider">
|
|
Registration Date
|
|
</h4>
|
|
</div>
|
|
<p className="text-lg font-semibold text-gray-900">
|
|
{formatDate(subscription.registrationDate)}
|
|
</p>
|
|
<p className="text-sm text-gray-500">Service created</p>
|
|
</div>
|
|
</div>
|
|
|
|
{/* Additional Details */}
|
|
<div className="mt-6 pt-6 border-t border-gray-100">
|
|
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
|
|
<div className="space-y-2">
|
|
<div className="flex items-center space-x-2">
|
|
<IdentificationIcon className="h-5 w-5 text-gray-400" />
|
|
<h4 className="text-sm font-medium text-gray-500">Service ID</h4>
|
|
</div>
|
|
<p className="text-base font-medium text-gray-900">{subscription.serviceId}</p>
|
|
</div>
|
|
|
|
{subscription.orderNumber && (
|
|
<div className="space-y-2">
|
|
<div className="flex items-center space-x-2">
|
|
<TagIcon className="h-5 w-5 text-gray-400" />
|
|
<h4 className="text-sm font-medium text-gray-500">Order Number</h4>
|
|
</div>
|
|
<p className="text-base font-medium text-gray-900">{subscription.orderNumber}</p>
|
|
</div>
|
|
)}
|
|
|
|
{subscription.groupName && (
|
|
<div className="space-y-2">
|
|
<h4 className="text-sm font-medium text-gray-500">Product Group</h4>
|
|
<p className="text-base font-medium text-gray-900">{subscription.groupName}</p>
|
|
</div>
|
|
)}
|
|
|
|
{subscription.paymentMethod && (
|
|
<div className="space-y-2">
|
|
<h4 className="text-sm font-medium text-gray-500">Payment Method</h4>
|
|
<p className="text-base font-medium text-gray-900">
|
|
{subscription.paymentMethod}
|
|
</p>
|
|
</div>
|
|
)}
|
|
</div>
|
|
</div>
|
|
|
|
{/* Custom Fields */}
|
|
{subscription.customFields && Object.keys(subscription.customFields).length > 0 && (
|
|
<div className="mt-6 pt-6 border-t border-gray-100">
|
|
<h4 className="text-sm font-medium text-gray-500 mb-3">Additional Information</h4>
|
|
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
|
|
{Object.entries(subscription.customFields).map(([key, value]) => (
|
|
<div key={key} className="space-y-1">
|
|
<p className="text-sm text-gray-500 capitalize">
|
|
{key.replace(/([A-Z])/g, " $1").trim()}
|
|
</p>
|
|
<p className="text-sm font-medium text-gray-900">{value}</p>
|
|
</div>
|
|
))}
|
|
</div>
|
|
</div>
|
|
)}
|
|
|
|
{/* Notes */}
|
|
{subscription.notes && (
|
|
<div className="mt-6 pt-6 border-t border-gray-100">
|
|
<h4 className="text-sm font-medium text-gray-500 mb-2">Notes</h4>
|
|
<p className="text-sm text-gray-700 bg-gray-50 rounded-md p-3">
|
|
{subscription.notes}
|
|
</p>
|
|
</div>
|
|
)}
|
|
</SubCard>
|
|
|
|
{/* Service-Specific Information */}
|
|
{showServiceSpecificSections && (
|
|
<>
|
|
{/* SIM Service Information */}
|
|
{isSimService(subscription.productName) && (
|
|
<SubCard title="SIM Service Information" icon={<ServerIcon className="h-5 w-5" />}>
|
|
<div className="bg-blue-50 border border-blue-200 rounded-lg p-4">
|
|
<div className="flex items-start space-x-3">
|
|
<div className="bg-blue-100 rounded-lg p-2">
|
|
<ServerIcon className="h-5 w-5 text-blue-600" />
|
|
</div>
|
|
<div>
|
|
<h4 className="text-sm font-semibold text-blue-900 mb-2">
|
|
SIM Management Available
|
|
</h4>
|
|
<p className="text-sm text-blue-800 mb-3">
|
|
This subscription includes SIM management features such as data usage
|
|
monitoring, top-up options, and service configuration.
|
|
</p>
|
|
<ul className="text-sm text-blue-700 space-y-1">
|
|
<li>• Real-time data usage tracking</li>
|
|
<li>• Data top-up and plan changes</li>
|
|
<li>• Service feature toggles</li>
|
|
<li>• SIM reissue options</li>
|
|
</ul>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</SubCard>
|
|
)}
|
|
|
|
{/* Internet Service Information */}
|
|
{isInternetService(subscription.productName) && (
|
|
<SubCard
|
|
title="Internet Service Information"
|
|
icon={<ServerIcon className="h-5 w-5" />}
|
|
>
|
|
<div className="bg-green-50 border border-green-200 rounded-lg p-4">
|
|
<div className="flex items-start space-x-3">
|
|
<div className="bg-green-100 rounded-lg p-2">
|
|
<ServerIcon className="h-5 w-5 text-green-600" />
|
|
</div>
|
|
<div>
|
|
<h4 className="text-sm font-semibold text-green-900 mb-2">
|
|
Internet Service Features
|
|
</h4>
|
|
<p className="text-sm text-green-800 mb-3">
|
|
Your internet service includes high-speed connectivity and support options.
|
|
</p>
|
|
<ul className="text-sm text-green-700 space-y-1">
|
|
<li>• High-speed internet connection</li>
|
|
<li>• 24/7 technical support</li>
|
|
<li>• Service monitoring</li>
|
|
<li>• Installation support</li>
|
|
</ul>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</SubCard>
|
|
)}
|
|
|
|
{/* VPN Service Information */}
|
|
{isVpnService(subscription.productName) && (
|
|
<SubCard title="VPN Service Information" icon={<ServerIcon className="h-5 w-5" />}>
|
|
<div className="bg-purple-50 border border-purple-200 rounded-lg p-4">
|
|
<div className="flex items-start space-x-3">
|
|
<div className="bg-purple-100 rounded-lg p-2">
|
|
<ServerIcon className="h-5 w-5 text-purple-600" />
|
|
</div>
|
|
<div>
|
|
<h4 className="text-sm font-semibold text-purple-900 mb-2">
|
|
VPN Service Features
|
|
</h4>
|
|
<p className="text-sm text-purple-800 mb-3">
|
|
Your VPN service provides secure and private internet access.
|
|
</p>
|
|
<ul className="text-sm text-purple-700 space-y-1">
|
|
<li>• Encrypted internet connection</li>
|
|
<li>• Multiple server locations</li>
|
|
<li>• No-logs policy</li>
|
|
<li>• Multi-device support</li>
|
|
</ul>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</SubCard>
|
|
)}
|
|
</>
|
|
)}
|
|
</div>
|
|
);
|
|
}
|
|
);
|
|
|
|
SubscriptionDetails.displayName = "SubscriptionDetails";
|