2025-09-05 15:39:43 +09:00
|
|
|
"use client";
|
|
|
|
|
|
|
|
|
|
import React, { useEffect, useMemo, useState } from "react";
|
2025-09-18 14:52:26 +09:00
|
|
|
import { apiClient } from "@/core/api";
|
2025-09-05 15:39:43 +09:00
|
|
|
|
|
|
|
|
interface SimFeatureTogglesProps {
|
|
|
|
|
subscriptionId: number;
|
|
|
|
|
voiceMailEnabled?: boolean;
|
|
|
|
|
callWaitingEnabled?: boolean;
|
|
|
|
|
internationalRoamingEnabled?: boolean;
|
|
|
|
|
networkType?: string; // '4G' | '5G'
|
|
|
|
|
onChanged?: () => void;
|
2025-09-05 18:22:55 +09:00
|
|
|
embedded?: boolean; // when true, render without outer card wrappers
|
2025-09-05 15:39:43 +09:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export function SimFeatureToggles({
|
|
|
|
|
subscriptionId,
|
|
|
|
|
voiceMailEnabled,
|
|
|
|
|
callWaitingEnabled,
|
|
|
|
|
internationalRoamingEnabled,
|
|
|
|
|
networkType,
|
|
|
|
|
onChanged,
|
2025-09-05 18:22:55 +09:00
|
|
|
embedded = false,
|
2025-09-05 15:39:43 +09:00
|
|
|
}: SimFeatureTogglesProps) {
|
|
|
|
|
// Initial values
|
2025-09-09 15:45:03 +09:00
|
|
|
const initial = useMemo(
|
|
|
|
|
() => ({
|
|
|
|
|
vm: !!voiceMailEnabled,
|
|
|
|
|
cw: !!callWaitingEnabled,
|
|
|
|
|
ir: !!internationalRoamingEnabled,
|
|
|
|
|
nt: networkType === "5G" ? "5G" : "4G",
|
|
|
|
|
}),
|
|
|
|
|
[voiceMailEnabled, callWaitingEnabled, internationalRoamingEnabled, networkType]
|
|
|
|
|
);
|
2025-09-05 15:39:43 +09:00
|
|
|
|
|
|
|
|
// Working values
|
|
|
|
|
const [vm, setVm] = useState(initial.vm);
|
|
|
|
|
const [cw, setCw] = useState(initial.cw);
|
|
|
|
|
const [ir, setIr] = useState(initial.ir);
|
2025-09-09 15:45:03 +09:00
|
|
|
const [nt, setNt] = useState<"4G" | "5G">(initial.nt as "4G" | "5G");
|
2025-09-05 15:39:43 +09:00
|
|
|
const [loading, setLoading] = useState(false);
|
|
|
|
|
const [error, setError] = useState<string | null>(null);
|
|
|
|
|
const [success, setSuccess] = useState<string | null>(null);
|
|
|
|
|
|
|
|
|
|
useEffect(() => {
|
|
|
|
|
setVm(initial.vm);
|
|
|
|
|
setCw(initial.cw);
|
|
|
|
|
setIr(initial.ir);
|
2025-09-09 15:45:03 +09:00
|
|
|
setNt(initial.nt as "4G" | "5G");
|
2025-09-05 18:22:55 +09:00
|
|
|
}, [initial.vm, initial.cw, initial.ir, initial.nt]);
|
2025-09-05 15:39:43 +09:00
|
|
|
|
|
|
|
|
const reset = () => {
|
|
|
|
|
setVm(initial.vm);
|
|
|
|
|
setCw(initial.cw);
|
|
|
|
|
setIr(initial.ir);
|
2025-09-09 15:45:03 +09:00
|
|
|
setNt(initial.nt as "4G" | "5G");
|
2025-09-05 15:39:43 +09:00
|
|
|
setError(null);
|
|
|
|
|
setSuccess(null);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const applyChanges = async () => {
|
|
|
|
|
setLoading(true);
|
|
|
|
|
setError(null);
|
|
|
|
|
setSuccess(null);
|
|
|
|
|
try {
|
2025-09-09 15:59:30 +09:00
|
|
|
const featurePayload: {
|
|
|
|
|
voiceMailEnabled?: boolean;
|
|
|
|
|
callWaitingEnabled?: boolean;
|
|
|
|
|
internationalRoamingEnabled?: boolean;
|
|
|
|
|
networkType?: "4G" | "5G";
|
|
|
|
|
} = {};
|
2025-09-05 15:39:43 +09:00
|
|
|
if (vm !== initial.vm) featurePayload.voiceMailEnabled = vm;
|
|
|
|
|
if (cw !== initial.cw) featurePayload.callWaitingEnabled = cw;
|
|
|
|
|
if (ir !== initial.ir) featurePayload.internationalRoamingEnabled = ir;
|
|
|
|
|
if (nt !== initial.nt) featurePayload.networkType = nt;
|
|
|
|
|
|
|
|
|
|
if (Object.keys(featurePayload).length > 0) {
|
2025-09-18 16:23:56 +09:00
|
|
|
await apiClient.POST("/subscriptions/{id}/sim/features", {
|
|
|
|
|
params: { path: { id: subscriptionId } },
|
|
|
|
|
body: featurePayload,
|
|
|
|
|
});
|
2025-09-05 15:39:43 +09:00
|
|
|
}
|
|
|
|
|
|
2025-09-09 15:45:03 +09:00
|
|
|
setSuccess("Changes submitted successfully");
|
2025-09-05 15:39:43 +09:00
|
|
|
onChanged?.();
|
2025-09-09 15:59:30 +09:00
|
|
|
} catch (e: unknown) {
|
2025-09-09 15:45:03 +09:00
|
|
|
setError(e instanceof Error ? e.message : "Failed to submit changes");
|
2025-09-05 15:39:43 +09:00
|
|
|
} finally {
|
|
|
|
|
setLoading(false);
|
|
|
|
|
setTimeout(() => setSuccess(null), 3000);
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
return (
|
|
|
|
|
<div className="space-y-6">
|
|
|
|
|
{/* Service Options */}
|
2025-09-09 15:45:03 +09:00
|
|
|
<div
|
|
|
|
|
className={`${embedded ? "" : "bg-white rounded-xl border border-gray-200 overflow-hidden"}`}
|
|
|
|
|
>
|
|
|
|
|
<div className={`${embedded ? "" : "p-6"} space-y-6`}>
|
2025-09-05 15:39:43 +09:00
|
|
|
{/* Voice Mail */}
|
|
|
|
|
<div className="flex flex-col lg:flex-row lg:items-center lg:justify-between space-y-4 lg:space-y-0 p-4 bg-gray-50 rounded-lg border border-gray-200">
|
|
|
|
|
<div className="flex-1">
|
|
|
|
|
<div className="flex items-center space-x-3">
|
|
|
|
|
<div className="bg-blue-100 rounded-lg p-2">
|
2025-09-09 15:45:03 +09:00
|
|
|
<svg
|
|
|
|
|
className="h-4 w-4 text-blue-600"
|
|
|
|
|
fill="none"
|
|
|
|
|
stroke="currentColor"
|
|
|
|
|
viewBox="0 0 24 24"
|
|
|
|
|
>
|
|
|
|
|
<path
|
|
|
|
|
strokeLinecap="round"
|
|
|
|
|
strokeLinejoin="round"
|
|
|
|
|
strokeWidth={2}
|
|
|
|
|
d="M3 5a2 2 0 012-2h3.28a1 1 0 01.948.684l1.498 4.493a1 1 0 01-.502 1.21l-2.257 1.13a11.042 11.042 0 005.516 5.516l1.13-2.257a1 1 0 011.21-.502l4.493 1.498a1 1 0 01.684.949V19a2 2 0 01-2 2h-1C9.716 21 3 14.284 3 6V5z"
|
|
|
|
|
/>
|
2025-09-05 15:39:43 +09:00
|
|
|
</svg>
|
|
|
|
|
</div>
|
|
|
|
|
<div>
|
|
|
|
|
<div className="text-sm font-semibold text-gray-900">Voice Mail</div>
|
|
|
|
|
<div className="text-xs text-gray-600">¥300/month</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
<div className="flex items-center space-x-4">
|
|
|
|
|
<div className="text-sm">
|
|
|
|
|
<span className="text-gray-500">Current: </span>
|
2025-09-09 15:45:03 +09:00
|
|
|
<span className={`font-medium ${initial.vm ? "text-green-600" : "text-gray-600"}`}>
|
|
|
|
|
{initial.vm ? "Enabled" : "Disabled"}
|
2025-09-05 15:39:43 +09:00
|
|
|
</span>
|
|
|
|
|
</div>
|
|
|
|
|
<div className="text-gray-400">→</div>
|
|
|
|
|
<select
|
2025-09-09 15:45:03 +09:00
|
|
|
value={vm ? "Enabled" : "Disabled"}
|
|
|
|
|
onChange={e => setVm(e.target.value === "Enabled")}
|
2025-09-05 15:39:43 +09:00
|
|
|
className="block w-32 rounded-lg border border-gray-200 bg-white text-sm text-gray-700 focus:border-blue-500 focus:ring-1 focus:ring-blue-500 focus:outline-none transition-colors hover:border-gray-300"
|
|
|
|
|
>
|
|
|
|
|
<option>Disabled</option>
|
|
|
|
|
<option>Enabled</option>
|
|
|
|
|
</select>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
{/* Call Waiting */}
|
|
|
|
|
<div className="flex flex-col lg:flex-row lg:items-center lg:justify-between space-y-4 lg:space-y-0 p-4 bg-gray-50 rounded-lg border border-gray-200">
|
|
|
|
|
<div className="flex-1">
|
|
|
|
|
<div className="flex items-center space-x-3">
|
|
|
|
|
<div className="bg-purple-100 rounded-lg p-2">
|
2025-09-09 15:45:03 +09:00
|
|
|
<svg
|
|
|
|
|
className="h-4 w-4 text-purple-600"
|
|
|
|
|
fill="none"
|
|
|
|
|
stroke="currentColor"
|
|
|
|
|
viewBox="0 0 24 24"
|
|
|
|
|
>
|
|
|
|
|
<path
|
|
|
|
|
strokeLinecap="round"
|
|
|
|
|
strokeLinejoin="round"
|
|
|
|
|
strokeWidth={2}
|
|
|
|
|
d="M12 8v4l3 3m6-3a9 9 0 11-18 0 9 9 0 0118 0z"
|
|
|
|
|
/>
|
2025-09-05 15:39:43 +09:00
|
|
|
</svg>
|
|
|
|
|
</div>
|
|
|
|
|
<div>
|
|
|
|
|
<div className="text-sm font-semibold text-gray-900">Call Waiting</div>
|
|
|
|
|
<div className="text-xs text-gray-600">¥300/month</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
<div className="flex items-center space-x-4">
|
|
|
|
|
<div className="text-sm">
|
|
|
|
|
<span className="text-gray-500">Current: </span>
|
2025-09-09 15:45:03 +09:00
|
|
|
<span className={`font-medium ${initial.cw ? "text-green-600" : "text-gray-600"}`}>
|
|
|
|
|
{initial.cw ? "Enabled" : "Disabled"}
|
2025-09-05 15:39:43 +09:00
|
|
|
</span>
|
|
|
|
|
</div>
|
|
|
|
|
<div className="text-gray-400">→</div>
|
|
|
|
|
<select
|
2025-09-09 15:45:03 +09:00
|
|
|
value={cw ? "Enabled" : "Disabled"}
|
|
|
|
|
onChange={e => setCw(e.target.value === "Enabled")}
|
2025-09-05 15:39:43 +09:00
|
|
|
className="block w-32 rounded-lg border border-gray-200 bg-white text-sm text-gray-700 focus:border-blue-500 focus:ring-1 focus:ring-blue-500 focus:outline-none transition-colors hover:border-gray-300"
|
|
|
|
|
>
|
|
|
|
|
<option>Disabled</option>
|
|
|
|
|
<option>Enabled</option>
|
|
|
|
|
</select>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
{/* International Roaming */}
|
|
|
|
|
<div className="flex flex-col lg:flex-row lg:items-center lg:justify-between space-y-4 lg:space-y-0 p-4 bg-gray-50 rounded-lg border border-gray-200">
|
|
|
|
|
<div className="flex-1">
|
|
|
|
|
<div className="flex items-center space-x-3">
|
|
|
|
|
<div className="bg-green-100 rounded-lg p-2">
|
2025-09-09 15:45:03 +09:00
|
|
|
<svg
|
|
|
|
|
className="h-4 w-4 text-green-600"
|
|
|
|
|
fill="none"
|
|
|
|
|
stroke="currentColor"
|
|
|
|
|
viewBox="0 0 24 24"
|
|
|
|
|
>
|
|
|
|
|
<path
|
|
|
|
|
strokeLinecap="round"
|
|
|
|
|
strokeLinejoin="round"
|
|
|
|
|
strokeWidth={2}
|
|
|
|
|
d="M3.055 11H5a2 2 0 012 2v1a2 2 0 002 2 2 2 0 012 2v2.945M8 3.935V5.5A2.5 2.5 0 0010.5 8h.5a2 2 0 012 2 2 2 0 104 0 2 2 0 012-2h1.064M15 20.488V18a2 2 0 012-2h3.064M21 12a9 9 0 11-18 0 9 9 0 0118 0z"
|
|
|
|
|
/>
|
2025-09-05 15:39:43 +09:00
|
|
|
</svg>
|
|
|
|
|
</div>
|
|
|
|
|
<div>
|
|
|
|
|
<div className="text-sm font-semibold text-gray-900">International Roaming</div>
|
|
|
|
|
<div className="text-xs text-gray-600">Global connectivity</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
<div className="flex items-center space-x-4">
|
|
|
|
|
<div className="text-sm">
|
|
|
|
|
<span className="text-gray-500">Current: </span>
|
2025-09-09 15:45:03 +09:00
|
|
|
<span className={`font-medium ${initial.ir ? "text-green-600" : "text-gray-600"}`}>
|
|
|
|
|
{initial.ir ? "Enabled" : "Disabled"}
|
2025-09-05 15:39:43 +09:00
|
|
|
</span>
|
|
|
|
|
</div>
|
|
|
|
|
<div className="text-gray-400">→</div>
|
|
|
|
|
<select
|
2025-09-09 15:45:03 +09:00
|
|
|
value={ir ? "Enabled" : "Disabled"}
|
|
|
|
|
onChange={e => setIr(e.target.value === "Enabled")}
|
2025-09-05 15:39:43 +09:00
|
|
|
className="block w-32 rounded-lg border border-gray-200 bg-white text-sm text-gray-700 focus:border-blue-500 focus:ring-1 focus:ring-blue-500 focus:outline-none transition-colors hover:border-gray-300"
|
|
|
|
|
>
|
|
|
|
|
<option>Disabled</option>
|
|
|
|
|
<option>Enabled</option>
|
|
|
|
|
</select>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
{/* Network Type */}
|
|
|
|
|
<div className="flex flex-col lg:flex-row lg:items-center lg:justify-between space-y-4 lg:space-y-0 p-4 bg-gray-50 rounded-lg border border-gray-200">
|
|
|
|
|
<div className="flex-1">
|
|
|
|
|
<div className="flex items-center space-x-3">
|
|
|
|
|
<div className="bg-orange-100 rounded-lg p-2">
|
2025-09-09 15:45:03 +09:00
|
|
|
<svg
|
|
|
|
|
className="h-4 w-4 text-orange-600"
|
|
|
|
|
fill="none"
|
|
|
|
|
stroke="currentColor"
|
|
|
|
|
viewBox="0 0 24 24"
|
|
|
|
|
>
|
|
|
|
|
<path
|
|
|
|
|
strokeLinecap="round"
|
|
|
|
|
strokeLinejoin="round"
|
|
|
|
|
strokeWidth={2}
|
|
|
|
|
d="M8.111 16.404a5.5 5.5 0 017.778 0M12 20h.01m-7.08-7.071c3.904-3.905 10.236-3.905 14.141 0M1.394 9.393c5.857-5.857 15.355-5.857 21.213 0"
|
|
|
|
|
/>
|
2025-09-05 15:39:43 +09:00
|
|
|
</svg>
|
|
|
|
|
</div>
|
|
|
|
|
<div>
|
|
|
|
|
<div className="text-sm font-semibold text-gray-900">Network Type</div>
|
|
|
|
|
<div className="text-xs text-gray-600">4G/5G connectivity</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
<div className="flex items-center space-x-4">
|
|
|
|
|
<div className="text-sm">
|
|
|
|
|
<span className="text-gray-500">Current: </span>
|
|
|
|
|
<span className="font-medium text-blue-600">{initial.nt}</span>
|
|
|
|
|
</div>
|
|
|
|
|
<div className="text-gray-400">→</div>
|
|
|
|
|
<select
|
|
|
|
|
value={nt}
|
2025-09-09 15:45:03 +09:00
|
|
|
onChange={e => setNt(e.target.value as "4G" | "5G")}
|
2025-09-05 15:39:43 +09:00
|
|
|
className="block w-20 rounded-lg border border-gray-200 bg-white text-sm text-gray-700 focus:border-blue-500 focus:ring-1 focus:ring-blue-500 focus:outline-none transition-colors hover:border-gray-300"
|
|
|
|
|
>
|
|
|
|
|
<option value="4G">4G</option>
|
|
|
|
|
<option value="5G">5G</option>
|
|
|
|
|
</select>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
{/* Notes and Actions */}
|
2025-09-09 15:45:03 +09:00
|
|
|
<div className={`${embedded ? "" : "bg-white rounded-xl border border-gray-200 p-6"}`}>
|
2025-09-05 15:39:43 +09:00
|
|
|
<div className="bg-yellow-50 border border-yellow-200 rounded-lg p-4 mb-6">
|
|
|
|
|
<div className="flex items-start">
|
2025-09-09 15:45:03 +09:00
|
|
|
<svg
|
|
|
|
|
className="h-5 w-5 text-yellow-600 mt-0.5 mr-3 flex-shrink-0"
|
|
|
|
|
fill="none"
|
|
|
|
|
stroke="currentColor"
|
|
|
|
|
viewBox="0 0 24 24"
|
|
|
|
|
>
|
|
|
|
|
<path
|
|
|
|
|
strokeLinecap="round"
|
|
|
|
|
strokeLinejoin="round"
|
|
|
|
|
strokeWidth={2}
|
|
|
|
|
d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"
|
|
|
|
|
/>
|
2025-09-05 15:39:43 +09:00
|
|
|
</svg>
|
|
|
|
|
<div className="space-y-2 text-sm text-yellow-800">
|
2025-09-09 15:45:03 +09:00
|
|
|
<p>
|
|
|
|
|
<strong>Important Notes:</strong>
|
|
|
|
|
</p>
|
2025-09-05 15:39:43 +09:00
|
|
|
<ul className="list-disc list-inside space-y-1 ml-4">
|
|
|
|
|
<li>Changes will take effect instantaneously (approx. 30min)</li>
|
|
|
|
|
<li>May require smartphone/device restart after changes are applied</li>
|
|
|
|
|
<li>5G requires a compatible smartphone/device. Will not function on 4G devices</li>
|
2025-09-09 15:45:03 +09:00
|
|
|
<li>
|
|
|
|
|
Changes to Voice Mail / Call Waiting must be requested before the 25th of the
|
|
|
|
|
month
|
|
|
|
|
</li>
|
2025-09-05 15:39:43 +09:00
|
|
|
</ul>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
{success && (
|
|
|
|
|
<div className="mb-4 bg-green-50 border border-green-200 rounded-lg p-4">
|
|
|
|
|
<div className="flex items-center">
|
2025-09-09 15:45:03 +09:00
|
|
|
<svg
|
|
|
|
|
className="h-5 w-5 text-green-500 mr-3"
|
|
|
|
|
fill="none"
|
|
|
|
|
stroke="currentColor"
|
|
|
|
|
viewBox="0 0 24 24"
|
|
|
|
|
>
|
|
|
|
|
<path
|
|
|
|
|
strokeLinecap="round"
|
|
|
|
|
strokeLinejoin="round"
|
|
|
|
|
strokeWidth={2}
|
|
|
|
|
d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z"
|
|
|
|
|
/>
|
2025-09-05 15:39:43 +09:00
|
|
|
</svg>
|
|
|
|
|
<p className="text-sm font-medium text-green-800">{success}</p>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
)}
|
|
|
|
|
|
|
|
|
|
{error && (
|
|
|
|
|
<div className="mb-4 bg-red-50 border border-red-200 rounded-lg p-4">
|
|
|
|
|
<div className="flex items-center">
|
2025-09-09 15:45:03 +09:00
|
|
|
<svg
|
|
|
|
|
className="h-5 w-5 text-red-500 mr-3"
|
|
|
|
|
fill="none"
|
|
|
|
|
stroke="currentColor"
|
|
|
|
|
viewBox="0 0 24 24"
|
|
|
|
|
>
|
|
|
|
|
<path
|
|
|
|
|
strokeLinecap="round"
|
|
|
|
|
strokeLinejoin="round"
|
|
|
|
|
strokeWidth={2}
|
|
|
|
|
d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-2.5L13.732 4c-.77-.833-1.964-.833-2.732 0L3.732 16.5c-.77.833.192 2.5 1.732 2.5z"
|
|
|
|
|
/>
|
2025-09-05 15:39:43 +09:00
|
|
|
</svg>
|
|
|
|
|
<p className="text-sm font-medium text-red-800">{error}</p>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
)}
|
|
|
|
|
|
|
|
|
|
<div className="flex flex-col sm:flex-row gap-3">
|
|
|
|
|
<button
|
2025-09-09 15:59:30 +09:00
|
|
|
onClick={() => void applyChanges()}
|
2025-09-05 15:39:43 +09:00
|
|
|
disabled={loading}
|
|
|
|
|
className="flex-1 inline-flex items-center justify-center px-6 py-3 border border-transparent rounded-lg text-sm font-semibold text-white bg-gradient-to-r from-blue-600 to-blue-700 hover:from-blue-700 hover:to-blue-800 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 disabled:opacity-50 disabled:cursor-not-allowed transition-all duration-200"
|
|
|
|
|
>
|
|
|
|
|
{loading ? (
|
|
|
|
|
<>
|
2025-09-09 15:45:03 +09:00
|
|
|
<svg
|
|
|
|
|
className="animate-spin -ml-1 mr-3 h-4 w-4 text-white"
|
|
|
|
|
fill="none"
|
|
|
|
|
viewBox="0 0 24 24"
|
|
|
|
|
>
|
|
|
|
|
<circle
|
|
|
|
|
cx="12"
|
|
|
|
|
cy="12"
|
|
|
|
|
r="10"
|
|
|
|
|
stroke="currentColor"
|
|
|
|
|
strokeWidth="4"
|
|
|
|
|
className="opacity-25"
|
|
|
|
|
></circle>
|
|
|
|
|
<path
|
|
|
|
|
fill="currentColor"
|
|
|
|
|
d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"
|
|
|
|
|
className="opacity-75"
|
|
|
|
|
></path>
|
2025-09-05 15:39:43 +09:00
|
|
|
</svg>
|
|
|
|
|
Applying Changes...
|
|
|
|
|
</>
|
|
|
|
|
) : (
|
|
|
|
|
<>
|
|
|
|
|
<svg className="h-4 w-4 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
2025-09-09 15:45:03 +09:00
|
|
|
<path
|
|
|
|
|
strokeLinecap="round"
|
|
|
|
|
strokeLinejoin="round"
|
|
|
|
|
strokeWidth={2}
|
|
|
|
|
d="M5 13l4 4L19 7"
|
|
|
|
|
/>
|
2025-09-05 15:39:43 +09:00
|
|
|
</svg>
|
|
|
|
|
Apply Changes
|
|
|
|
|
</>
|
|
|
|
|
)}
|
|
|
|
|
</button>
|
|
|
|
|
<button
|
2025-09-09 15:59:30 +09:00
|
|
|
onClick={() => reset()}
|
2025-09-05 15:39:43 +09:00
|
|
|
disabled={loading}
|
|
|
|
|
className="inline-flex items-center justify-center px-6 py-3 border border-gray-300 rounded-lg text-sm font-semibold text-gray-700 bg-white hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 disabled:opacity-50 disabled:cursor-not-allowed transition-colors duration-200"
|
|
|
|
|
>
|
|
|
|
|
<svg className="h-4 w-4 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
2025-09-09 15:45:03 +09:00
|
|
|
<path
|
|
|
|
|
strokeLinecap="round"
|
|
|
|
|
strokeLinejoin="round"
|
|
|
|
|
strokeWidth={2}
|
|
|
|
|
d="M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15"
|
|
|
|
|
/>
|
2025-09-05 15:39:43 +09:00
|
|
|
</svg>
|
|
|
|
|
Reset
|
|
|
|
|
</button>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
);
|
|
|
|
|
}
|