Refactor OrderStatusPage and SimChangePlanPage for improved readability and functionality

- Removed duplicate text and unnecessary comments in OrderStatusPage for cleaner code.
- Moved the status section to the top for better user experience.
- Simplified the handling of item types in SimChangePlanPage and added new state variables for global IP assignment and scheduling.
- Streamlined form submission and error handling in SimChangePlanPage, enhancing user feedback.
This commit is contained in:
T. Narantuya 2025-09-11 16:21:47 +09:00
parent 4686bce071
commit 86cd636b87
2 changed files with 10 additions and 157 deletions

View File

@ -206,8 +206,6 @@ export default function OrderStatusPage() {
<p className="text-green-800 mb-3"> <p className="text-green-800 mb-3">
Your order has been created and submitted for processing. We will notify you as soon Your order has been created and submitted for processing. We will notify you as soon
as it&apos;s approved and ready for activation. as it&apos;s approved and ready for activation.
Your order has been created and submitted for processing. We will notify you as soon
as it&apos;s approved and ready for activation.
</p> </p>
<div className="text-sm text-green-700"> <div className="text-sm text-green-700">
<p className="mb-1"> <p className="mb-1">
@ -226,7 +224,6 @@ export default function OrderStatusPage() {
)} )}
{/* Status Section - Moved to top */} {/* Status Section - Moved to top */}
{data &&
{data && {data &&
(() => { (() => {
const statusInfo = getDetailedStatusInfo( const statusInfo = getDetailedStatusInfo(
@ -245,11 +242,9 @@ export default function OrderStatusPage() {
: "neutral"; : "neutral";
return ( return (
<SubCard
<SubCard <SubCard
className="mb-9" className="mb-9"
header={<h3 className="text-xl font-bold text-gray-900">Status</h3>} header={<h3 className="text-xl font-bold text-gray-900">Status</h3>}
header={<h3 className="text-xl font-bold text-gray-900">Status</h3>}
> >
<div className="flex flex-col sm:flex-row items-start sm:items-center justify-between gap-3 mb-6"> <div className="flex flex-col sm:flex-row items-start sm:items-center justify-between gap-3 mb-6">
<div className="text-gray-700 text-lg sm:text-xl">{statusInfo.description}</div> <div className="text-gray-700 text-lg sm:text-xl">{statusInfo.description}</div>
@ -259,7 +254,6 @@ export default function OrderStatusPage() {
/> />
</div> </div>
{/* Highlighted Next Steps Section */} {/* Highlighted Next Steps Section */}
{statusInfo.nextAction && ( {statusInfo.nextAction && (
<div className="bg-blue-50 border-2 border-blue-200 rounded-xl p-4 mb-4 shadow-sm"> <div className="bg-blue-50 border-2 border-blue-200 rounded-xl p-4 mb-4 shadow-sm">
@ -271,7 +265,6 @@ export default function OrderStatusPage() {
</div> </div>
)} )}
{statusInfo.timeline && ( {statusInfo.timeline && (
<div className="bg-gray-50 rounded-lg p-3 border border-gray-200"> <div className="bg-gray-50 rounded-lg p-3 border border-gray-200">
<p className="text-sm text-gray-600"> <p className="text-sm text-gray-600">
@ -282,16 +275,12 @@ export default function OrderStatusPage() {
</SubCard> </SubCard>
); );
})()} })()}
})()}
{/* Combined Service Overview and Products */} {/* Combined Service Overview and Products */}
{data && ( {data && (
<div className="bg-white border rounded-2xl p-4 sm:p-8 mb-8"> <div className="bg-white border rounded-2xl p-4 sm:p-8 mb-8">
{/* Service Header */} {/* Service Header */}
<div className="flex flex-col sm:flex-row items-start gap-4 sm:gap-6 mb-6"> <div className="flex flex-col sm:flex-row items-start gap-4 sm:gap-6 mb-6">
<div className="flex items-center text-3xl sm:text-4xl">
{getServiceTypeIcon(data.orderType)}
</div>
<div className="flex items-center text-3xl sm:text-4xl"> <div className="flex items-center text-3xl sm:text-4xl">
{getServiceTypeIcon(data.orderType)} {getServiceTypeIcon(data.orderType)}
</div> </div>
@ -366,7 +355,6 @@ export default function OrderStatusPage() {
const aIsInstallation = a.product.itemClass === "Installation"; const aIsInstallation = a.product.itemClass === "Installation";
const bIsInstallation = b.product.itemClass === "Installation"; const bIsInstallation = b.product.itemClass === "Installation";
if (aIsService && !bIsService) return -1; if (aIsService && !bIsService) return -1;
if (!aIsService && bIsService) return 1; if (!aIsService && bIsService) return 1;
if (aIsInstallation && !bIsInstallation) return -1; if (aIsInstallation && !bIsInstallation) return -1;
@ -376,53 +364,7 @@ export default function OrderStatusPage() {
.map(item => { .map(item => {
// Use the actual Item_Class__c values from Salesforce documentation // Use the actual Item_Class__c values from Salesforce documentation
const itemClass = item.product.itemClass; const itemClass = item.product.itemClass;
// Use the actual Item_Class__c values from Salesforce documentation
const itemClass = item.product.itemClass;
// Get appropriate icon and color based on item type and billing cycle
const getItemTypeInfo = () => {
const isMonthly = item.product.billingCycle === "Monthly";
const isService = itemClass === "Service";
const isInstallation = itemClass === "Installation";
if (isService && isMonthly) {
// Main service products - Blue theme
return {
icon: <StarIcon className="h-4 w-4" />,
bg: "bg-blue-50 border-blue-200",
iconBg: "bg-blue-100 text-blue-600",
label: itemClass || "Service",
labelColor: "text-blue-600",
};
} else if (isInstallation) {
// Installation items - Green theme
return {
icon: <WrenchScrewdriverIcon className="h-4 w-4" />,
bg: "bg-green-50 border-green-200",
iconBg: "bg-green-100 text-green-600",
label: itemClass || "Installation",
labelColor: "text-green-600",
};
} else if (isMonthly) {
// Other monthly products - Blue theme
return {
icon: <StarIcon className="h-4 w-4" />,
bg: "bg-blue-50 border-blue-200",
iconBg: "bg-blue-100 text-blue-600",
label: itemClass || "Service",
labelColor: "text-blue-600",
};
} else {
// One-time products - Orange theme
return {
icon: <CubeIcon className="h-4 w-4" />,
bg: "bg-orange-50 border-orange-200",
iconBg: "bg-orange-100 text-orange-600",
label: itemClass || "Add-on",
labelColor: "text-orange-600",
};
}
};
// Get appropriate icon and color based on item type and billing cycle // Get appropriate icon and color based on item type and billing cycle
const getItemTypeInfo = () => { const getItemTypeInfo = () => {
const isMonthly = item.product.billingCycle === "Monthly"; const isMonthly = item.product.billingCycle === "Monthly";
@ -468,21 +410,8 @@ export default function OrderStatusPage() {
} }
}; };
const typeInfo = getItemTypeInfo();
const typeInfo = getItemTypeInfo(); const typeInfo = getItemTypeInfo();
return (
<div
key={item.id}
className={`rounded-lg p-4 border ${typeInfo.bg} transition-shadow hover:shadow-sm`}
>
<div className="flex flex-col sm:flex-row justify-between items-start gap-3">
<div className="flex items-start gap-3 flex-1">
<div
className={`w-8 h-8 rounded-full flex items-center justify-center text-sm ${typeInfo.iconBg} flex-shrink-0`}
>
{typeInfo.icon}
</div>
return ( return (
<div <div
key={item.id} key={item.id}
@ -496,17 +425,6 @@ export default function OrderStatusPage() {
{typeInfo.icon} {typeInfo.icon}
</div> </div>
<div className="flex-1 min-w-0">
<div className="flex items-center gap-2 mb-1 flex-wrap">
<h3 className="font-semibold text-gray-900 truncate flex-1 min-w-0">
{item.product.name}
</h3>
<span
className={`text-xs px-2 py-1 rounded-full font-medium ${typeInfo.bg} ${typeInfo.labelColor}`}
>
{typeInfo.label}
</span>
</div>
<div className="flex-1 min-w-0"> <div className="flex-1 min-w-0">
<div className="flex items-center gap-2 mb-1 flex-wrap"> <div className="flex items-center gap-2 mb-1 flex-wrap">
<h3 className="font-semibold text-gray-900 truncate flex-1 min-w-0"> <h3 className="font-semibold text-gray-900 truncate flex-1 min-w-0">
@ -530,32 +448,6 @@ export default function OrderStatusPage() {
</div> </div>
</div> </div>
</div> </div>
<div className="flex flex-wrap items-center gap-3 text-sm text-gray-600">
<span className="font-medium">{item.product.billingCycle}</span>
{item.quantity > 1 && <span>Qty: {item.quantity}</span>}
{item.product.itemClass && (
<span className="text-xs bg-gray-100 px-2 py-1 rounded">
{item.product.itemClass}
</span>
)}
</div>
</div>
</div>
<div className="text-left sm:text-right ml-0 sm:ml-3 mt-2 sm:mt-0 flex-shrink-0 sm:w-32">
{item.totalPrice && (
<div className="font-semibold text-gray-900 tabular-nums">
¥{item.totalPrice.toLocaleString()}
</div>
)}
<div className="text-xs text-gray-500">
{item.product.billingCycle === "Monthly" ? "/month" : "one-time"}
</div>
</div>
</div>
</div>
);
})}
<div className="text-left sm:text-right ml-0 sm:ml-3 mt-2 sm:mt-0 flex-shrink-0 sm:w-32"> <div className="text-left sm:text-right ml-0 sm:ml-3 mt-2 sm:mt-0 flex-shrink-0 sm:w-32">
{item.totalPrice && ( {item.totalPrice && (
@ -577,9 +469,6 @@ export default function OrderStatusPage() {
<div className="flex items-start gap-2"> <div className="flex items-start gap-2">
<ExclamationTriangleIcon className="h-4 w-4 text-yellow-600 flex-shrink-0" /> <ExclamationTriangleIcon className="h-4 w-4 text-yellow-600 flex-shrink-0" />
<div> <div>
<p className="text-sm font-medium text-yellow-900">
Additional fees may apply
</p>
<p className="text-sm font-medium text-yellow-900"> <p className="text-sm font-medium text-yellow-900">
Additional fees may apply Additional fees may apply
</p> </p>

View File

@ -2,12 +2,12 @@
import { useState, useMemo } from "react"; import { useState, useMemo } from "react";
import Link from "next/link"; import Link from "next/link";
import { DashboardLayout } from "@/components/layout/dashboard-layout";
import { useParams } from "next/navigation"; import { useParams } from "next/navigation";
import { authenticatedApi } from "@/lib/api"; import { authenticatedApi } from "@/lib/api";
const PLAN_CODES = ["PASI_5G", "PASI_10G", "PASI_25G", "PASI_50G"] as const; const PLAN_CODES = ["PASI_5G", "PASI_10G", "PASI_25G", "PASI_50G"] as const;
type PlanCode = (typeof PLAN_CODES)[number]; type PlanCode = (typeof PLAN_CODES)[number];
type PlanCode = (typeof PLAN_CODES)[number];
const PLAN_LABELS: Record<PlanCode, string> = { const PLAN_LABELS: Record<PlanCode, string> = {
PASI_5G: "5GB", PASI_5G: "5GB",
PASI_10G: "10GB", PASI_10G: "10GB",
@ -20,14 +20,12 @@ export default function SimChangePlanPage() {
const subscriptionId = parseInt(params.id as string); const subscriptionId = parseInt(params.id as string);
const [currentPlanCode] = useState<string>(""); const [currentPlanCode] = useState<string>("");
const [newPlanCode, setNewPlanCode] = useState<"" | PlanCode>(""); const [newPlanCode, setNewPlanCode] = useState<"" | PlanCode>("");
const [assignGlobalIp, setAssignGlobalIp] = useState(false);
const [scheduledAt, setScheduledAt] = useState("");
const [message, setMessage] = useState<string | null>(null); const [message, setMessage] = useState<string | null>(null);
const [error, setError] = useState<string | null>(null); const [error, setError] = useState<string | null>(null);
const [loading, setLoading] = useState(false); const [loading, setLoading] = useState(false);
const options = useMemo(
() => (PLAN_CODES as readonly PlanCode[]).filter(c => c !== (currentPlanCode as PlanCode)),
[currentPlanCode]
);
const options = useMemo( const options = useMemo(
() => (PLAN_CODES as readonly PlanCode[]).filter(c => c !== (currentPlanCode as PlanCode)), () => (PLAN_CODES as readonly PlanCode[]).filter(c => c !== (currentPlanCode as PlanCode)),
[currentPlanCode] [currentPlanCode]
@ -45,9 +43,11 @@ export default function SimChangePlanPage() {
try { try {
await authenticatedApi.post(`/subscriptions/${subscriptionId}/sim/change-plan`, { await authenticatedApi.post(`/subscriptions/${subscriptionId}/sim/change-plan`, {
newPlanCode, newPlanCode,
assignGlobalIp,
scheduledAt: scheduledAt ? scheduledAt.replace(/-/g, "") : undefined,
}); });
setMessage("Plan change submitted successfully"); setMessage("Plan change submitted successfully");
} catch (e: unknown) { } catch (e: any) {
setError(e instanceof Error ? e.message : "Failed to change plan"); setError(e instanceof Error ? e.message : "Failed to change plan");
} finally { } finally {
setLoading(false); setLoading(false);
@ -55,7 +55,8 @@ export default function SimChangePlanPage() {
}; };
return ( return (
<div className="max-w-3xl mx-auto p-6"> <DashboardLayout>
<div className="max-w-3xl mx-auto p-6">
<div className="mb-4"> <div className="mb-4">
<Link <Link
href={`/subscriptions/${subscriptionId}#sim-management`} href={`/subscriptions/${subscriptionId}#sim-management`}
@ -63,12 +64,6 @@ export default function SimChangePlanPage() {
> >
Back to SIM Management Back to SIM Management
</Link> </Link>
<Link
href={`/subscriptions/${subscriptionId}#sim-management`}
className="text-blue-600 hover:text-blue-700"
>
Back to SIM Management
</Link>
</div> </div>
<div className="bg-white rounded-xl border border-gray-200 p-6"> <div className="bg-white rounded-xl border border-gray-200 p-6">
<h1 className="text-xl font-semibold text-gray-900 mb-1">Change Plan</h1> <h1 className="text-xl font-semibold text-gray-900 mb-1">Change Plan</h1>
@ -87,29 +82,13 @@ export default function SimChangePlanPage() {
{error} {error}
</div> </div>
)} )}
<p className="text-sm text-gray-600 mb-6">
Change Plan: Switch to a different data plan. Important: Plan changes must be requested
before the 25th of the month. Changes will take effect on the 1st of the following
month.
</p>
{message && (
<div className="mb-4 text-green-700 bg-green-50 border border-green-200 rounded p-3">
{message}
</div>
)}
{error && (
<div className="mb-4 text-red-700 bg-red-50 border border-red-200 rounded p-3">
{error}
</div>
)}
<form onSubmit={e => void submit(e)} className="space-y-6"> <form onSubmit={submit} className="space-y-6">
<div> <div>
<label className="block text-sm font-medium text-gray-700 mb-2">New Plan</label> <label className="block text-sm font-medium text-gray-700 mb-2">New Plan</label>
<select <select
value={newPlanCode} value={newPlanCode}
onChange={e => setNewPlanCode(e.target.value as PlanCode)} onChange={e => setNewPlanCode(e.target.value as PlanCode)}
onChange={e => setNewPlanCode(e.target.value as PlanCode)}
className="w-full px-3 py-2 border border-gray-300 rounded-md" className="w-full px-3 py-2 border border-gray-300 rounded-md"
> >
<option value="">Choose a plan</option> <option value="">Choose a plan</option>
@ -117,9 +96,6 @@ export default function SimChangePlanPage() {
<option key={code} value={code}> <option key={code} value={code}>
{PLAN_LABELS[code]} {PLAN_LABELS[code]}
</option> </option>
<option key={code} value={code}>
{PLAN_LABELS[code]}
</option>
))} ))}
</select> </select>
</div> </div>
@ -163,22 +139,10 @@ export default function SimChangePlanPage() {
> >
Back Back
</Link> </Link>
<button
type="submit"
disabled={loading}
className="px-4 py-2 rounded-md bg-blue-600 text-white text-sm disabled:opacity-50"
>
{loading ? "Processing…" : "Submit Plan Change"}
</button>
<Link
href={`/subscriptions/${subscriptionId}#sim-management`}
className="px-4 py-2 rounded-md border border-gray-300 text-sm text-gray-700 bg-white hover:bg-gray-50"
>
Back
</Link>
</div> </div>
</form> </form>
</div> </div>
</div> </div>
</DashboardLayout>
); );
} }