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:
parent
4686bce071
commit
86cd636b87
@ -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's approved and ready for activation.
|
as it's approved and ready for activation.
|
||||||
Your order has been created and submitted for processing. We will notify you as soon
|
|
||||||
as it'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>
|
||||||
|
|||||||
@ -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>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user