222 lines
8.6 KiB
TypeScript
Raw Normal View History

"use client";
import React, { useMemo, useState } from "react";
import { ArrowPathIcon, XMarkIcon } from "@heroicons/react/24/outline";
import { simActionsService } from "@/features/subscriptions/services/sim-actions.service";
type SimKind = "physical" | "esim";
interface ReissueSimModalProps {
subscriptionId: number;
currentSimType: SimKind;
onClose: () => void;
onSuccess: () => void;
onError: (message: string) => void;
}
const IMPORTANT_POINTS: string[] = [
"The reissue request cannot be reversed.",
"Service to the existing SIM will be terminated with immediate effect.",
"A fee of 1,500 yen + tax will be incurred.",
"For physical SIM: allow approximately 3-5 business days for shipping.",
"For eSIM: activation typically completes within 30-60 minutes after processing.",
];
const EID_HELP = "Enter the 32-digit EID (numbers only). Leave blank to reuse Freebit's generated EID.";
export function ReissueSimModal({
subscriptionId,
currentSimType,
onClose,
onSuccess,
onError,
}: ReissueSimModalProps) {
const [selectedSimType, setSelectedSimType] = useState<SimKind>(currentSimType);
const [newEid, setNewEid] = useState("");
const [submitting, setSubmitting] = useState(false);
const [validationError, setValidationError] = useState<string | null>(null);
const isEsimSelected = selectedSimType === "esim";
const isPhysicalSelected = selectedSimType === "physical";
const disableSubmit = useMemo(() => {
if (isPhysicalSelected) {
return false; // Allow click to show guidance message
}
if (!isEsimSelected) {
return true;
}
if (!newEid) {
return false; // Optional backend supports auto EID
}
return !/^\d{32}$/.test(newEid.trim());
}, [isPhysicalSelected, isEsimSelected, newEid]);
const handleSubmit = async () => {
if (isPhysicalSelected) {
setValidationError(
"Physical SIM reissue cannot be requested online yet. Please contact support for assistance."
);
return;
}
if (isEsimSelected && newEid && !/^\d{32}$/.test(newEid.trim())) {
setValidationError("EID must be 32 digits.");
return;
}
setValidationError(null);
setSubmitting(true);
try {
await simActionsService.reissueEsim(String(subscriptionId), {
newEid: newEid.trim() || undefined,
});
onSuccess();
} catch (error: unknown) {
const message = error instanceof Error ? error.message : "Failed to submit reissue request";
onError(message);
} finally {
setSubmitting(false);
}
};
return (
<div className="fixed inset-0 z-50 flex items-center justify-center">
<div className="absolute inset-0 bg-gray-500 bg-opacity-75" aria-hidden="true" />
<div className="relative z-10 w-full max-w-2xl rounded-lg border border-gray-200 bg-white shadow-2xl">
<div className="px-6 pt-6 pb-4 sm:px-8 sm:pb-6">
<div className="flex items-start justify-between">
<div className="flex items-center gap-3">
<span className="flex h-10 w-10 items-center justify-center rounded-full bg-green-100">
<ArrowPathIcon className="h-6 w-6 text-green-600" />
</span>
<div>
<h3 className="text-lg font-semibold text-gray-900">Reissue SIM</h3>
<p className="text-sm text-gray-600">
Submit a reissue request for your SIM. Review the important information before continuing.
</p>
</div>
</div>
<button
onClick={onClose}
className="text-gray-400 transition hover:text-gray-600"
aria-label="Close reissue SIM modal"
type="button"
>
<XMarkIcon className="h-5 w-5" />
</button>
</div>
<div className="mt-6 rounded-lg border border-amber-200 bg-amber-50 p-4">
<h4 className="text-sm font-semibold text-amber-800">Important information</h4>
<ul className="mt-2 list-disc space-y-1 pl-5 text-sm text-amber-900">
{IMPORTANT_POINTS.map(point => (
<li key={point}>{point}</li>
))}
</ul>
</div>
<div className="mt-6 grid gap-6 md:grid-cols-2">
<div>
<label className="block text-sm font-medium text-gray-700">Select SIM type</label>
<div className="mt-3 space-y-2">
<label className="flex items-start gap-3 rounded-lg border border-gray-200 p-3">
<input
type="radio"
name="sim-type"
value="physical"
checked={selectedSimType === "physical"}
onChange={() => setSelectedSimType("physical")}
className="mt-1"
/>
<div>
<p className="text-sm font-medium text-gray-900">Physical SIM</p>
<p className="text-xs text-gray-500">
Well ship a replacement SIM card. Currently, online requests are not available; contact support to proceed.
</p>
</div>
</label>
<label className="flex items-start gap-3 rounded-lg border border-gray-200 p-3">
<input
type="radio"
name="sim-type"
value="esim"
checked={selectedSimType === "esim"}
onChange={() => setSelectedSimType("esim")}
className="mt-1"
/>
<div>
<p className="text-sm font-medium text-gray-900">eSIM</p>
<p className="text-xs text-gray-500">
Generate a new eSIM activation profile. Youll receive new QR code details once processing completes.
</p>
</div>
</label>
</div>
</div>
<div className="rounded-lg border border-gray-200 p-4 text-sm text-gray-600">
<p>
Current SIM type: <strong className="uppercase">{currentSimType}</strong>
</p>
<p className="mt-2">
The selection above lets you specify which type of replacement you need. If you choose a physical SIM, a support agent will contact you to finalise the process.
</p>
</div>
</div>
{isEsimSelected && (
<div className="mt-6">
<label htmlFor="new-eid" className="block text-sm font-medium text-gray-700">
New EID (optional)
</label>
<input
id="new-eid"
type="text"
inputMode="numeric"
pattern="[0-9]*"
value={newEid}
onChange={event => {
setNewEid(event.target.value.replace(/\s+/g, ""));
setValidationError(null);
}}
placeholder="Enter 32-digit EID"
className="mt-1 block w-full rounded-md border border-gray-300 px-3 py-2 text-sm shadow-sm focus:border-blue-500 focus:outline-none focus:ring-1 focus:ring-blue-500"
/>
<p className="mt-1 text-xs text-gray-500">{EID_HELP}</p>
</div>
)}
{validationError && (
<p className="mt-4 rounded-md border border-red-200 bg-red-50 px-3 py-2 text-sm text-red-600">
{validationError}
</p>
)}
</div>
<div className="flex flex-col gap-3 border-t border-gray-200 bg-gray-50 p-4 sm:flex-row sm:justify-end sm:px-6">
<button
type="button"
onClick={() => void handleSubmit()}
disabled={disableSubmit || submitting}
className="inline-flex justify-center rounded-md px-4 py-2 text-sm font-semibold text-white shadow-sm disabled:cursor-not-allowed disabled:opacity-70"
style={{ background: "linear-gradient(90deg, #16a34a, #15803d)" }}
>
{submitting ? "Submitting..." : isPhysicalSelected ? "Contact Support" : "Confirm Reissue"}
</button>
<button
type="button"
onClick={onClose}
disabled={submitting}
className="inline-flex justify-center rounded-md border border-gray-300 bg-white px-4 py-2 text-sm font-medium text-gray-700 shadow-sm transition hover:bg-gray-100 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500"
>
Cancel
</button>
</div>
</div>
</div>
);
}