diff --git a/apps/bff/src/common/config/field-map.ts b/apps/bff/src/common/config/field-map.ts
index ecc2344f..b62cde0a 100644
--- a/apps/bff/src/common/config/field-map.ts
+++ b/apps/bff/src/common/config/field-map.ts
@@ -176,11 +176,13 @@ export function getSalesforceFieldMap(): SalesforceFieldMap {
// Billing address snapshot fields
billing: {
- street: process.env.ORDER_BILL_TO_STREET_FIELD || "BillToStreet",
- city: process.env.ORDER_BILL_TO_CITY_FIELD || "BillToCity",
- state: process.env.ORDER_BILL_TO_STATE_FIELD || "BillToState",
- postalCode: process.env.ORDER_BILL_TO_POSTAL_CODE_FIELD || "BillToPostalCode",
- country: process.env.ORDER_BILL_TO_COUNTRY_FIELD || "BillToCountry",
+ // Default to standard Order BillingAddress components
+ // Env overrides maintain backward compatibility if orgs used custom fields
+ street: process.env.ORDER_BILL_TO_STREET_FIELD || "BillingStreet",
+ city: process.env.ORDER_BILL_TO_CITY_FIELD || "BillingCity",
+ state: process.env.ORDER_BILL_TO_STATE_FIELD || "BillingState",
+ postalCode: process.env.ORDER_BILL_TO_POSTAL_CODE_FIELD || "BillingPostalCode",
+ country: process.env.ORDER_BILL_TO_COUNTRY_FIELD || "BillingCountry",
},
},
orderItem: {
diff --git a/apps/portal/src/app/orders/[id]/page.tsx b/apps/portal/src/app/orders/[id]/page.tsx
index 41ffb570..50d49348 100644
--- a/apps/portal/src/app/orders/[id]/page.tsx
+++ b/apps/portal/src/app/orders/[id]/page.tsx
@@ -3,7 +3,7 @@
import { useEffect, useState } from "react";
import { useParams, useSearchParams } from "next/navigation";
import { PageLayout } from "@/components/layout/page-layout";
-import { ClipboardDocumentCheckIcon, CheckCircleIcon } from "@heroicons/react/24/outline";
+import { ClipboardDocumentCheckIcon, CheckCircleIcon, WifiIcon, DevicePhoneMobileIcon, LockClosedIcon, CubeIcon, StarIcon, WrenchScrewdriverIcon, PlusIcon, BoltIcon, ExclamationTriangleIcon, EnvelopeIcon, PhoneIcon } from "@heroicons/react/24/outline";
import { SubCard } from "@/components/ui/sub-card";
import { StatusPill } from "@/components/ui/status-pill";
import { authenticatedApi } from "@/lib/api";
@@ -71,8 +71,8 @@ const getDetailedStatusInfo = (
color: "text-blue-800",
bgColor: "bg-blue-50 border-blue-200",
description: "Our team is reviewing your order details",
- nextAction: "We'll contact you within 1-2 business days with next steps",
- timeline: "Review typically takes 1-2 business days",
+ nextAction: "We will contact you within 1 business day with next steps",
+ timeline: "Review typically takes 1 business day",
};
}
@@ -111,20 +111,20 @@ const getDetailedStatusInfo = (
color: "text-gray-800",
bgColor: "bg-gray-50 border-gray-200",
description: "Your order is being processed",
- timeline: "We'll update you as progress is made",
+ timeline: "We will update you as progress is made",
};
};
const getServiceTypeIcon = (orderType?: string) => {
switch (orderType) {
case "Internet":
- return "🌐";
+ return ;
case "SIM":
- return "📱";
+ return ;
case "VPN":
- return "🔒";
+ return ;
default:
- return "📦";
+ return ;
}
};
@@ -182,7 +182,7 @@ export default function OrderStatusPage() {
{/* Success Banner for New Orders */}
{isNewOrder && (
-
+
@@ -190,7 +190,7 @@ export default function OrderStatusPage() {
Order Submitted Successfully!
- Your order has been created and submitted for processing. We'll notify you as
+ Your order has been created and submitted for processing. We will notify you as
soon as it's approved and ready for activation.
@@ -198,9 +198,9 @@ export default function OrderStatusPage() {
What happens next:
- - Our team will review your order (usually within 1-2 business days)
+ - Our team will review your order (within 1 business day)
- You'll receive an email confirmation once approved
- - We'll schedule activation based on your preferences
+ - We will schedule activation based on your preferences
- This page will update automatically as your order progresses
@@ -209,8 +209,8 @@ export default function OrderStatusPage() {
)}
- {/* Service Overview */}
- {data &&
+ {/* Status Section - Moved to top */}
+ {data && (
(() => {
const statusInfo = getDetailedStatusInfo(
data.status,
@@ -218,7 +218,6 @@ export default function OrderStatusPage() {
data.activationType,
data.scheduledAt
);
- const serviceIcon = getServiceTypeIcon(data.orderType);
const statusVariant = statusInfo.label.includes("Active")
? "success"
@@ -229,268 +228,269 @@ export default function OrderStatusPage() {
: "neutral";
return (
-
- {/* Service Header */}
-
-
{serviceIcon}
-
-
- {data.orderType} Service
-
-
- Order #{data.orderNumber || data.id.slice(-8)} • Placed{" "}
- {new Date(data.createdDate).toLocaleDateString("en-US", {
- weekday: "long",
- month: "long",
- day: "numeric",
- year: "numeric",
- })}
-
-
-
- {data.items &&
- data.items.length > 0 &&
- (() => {
- const totals = calculateDetailedTotals(data.items);
-
- return (
-
-
- {totals.monthlyTotal > 0 && (
-
-
- ¥{totals.monthlyTotal.toLocaleString()}
-
-
per month
-
- )}
-
- {totals.oneTimeTotal > 0 && (
-
-
- ¥{totals.oneTimeTotal.toLocaleString()}
-
-
one-time
-
- )}
-
- {/* Fallback to TotalAmount if no items or calculation fails */}
- {totals.monthlyTotal === 0 &&
- totals.oneTimeTotal === 0 &&
- data.totalAmount && (
-
-
- ¥{data.totalAmount.toLocaleString()}
-
-
total amount
-
- )}
-
-
- );
- })()}
+
Status
+ }
+ >
+
+
{statusInfo.description}
+
-
- {/* Status Card (standardized) */}
-
- }
- >
- {statusInfo.description}
- {statusInfo.nextAction && (
-
-
Next Steps
-
{statusInfo.nextAction}
+
+ {/* Highlighted Next Steps Section */}
+ {statusInfo.nextAction && (
+
+
- )}
- {statusInfo.timeline && (
+
{statusInfo.nextAction}
+
+ )}
+
+ {statusInfo.timeline && (
+
Timeline: {statusInfo.timeline}
- )}
-
-
- );
- })()}
-
- {/* Service Details */}
- {data?.items && data.items.length > 0 && (
-
-
- {data.items.map(item => {
- // Use the actual Item_Class__c values from Salesforce documentation
- const itemClass = item.product.itemClass;
-
- // Get appropriate icon and color based on actual item class
- const getItemTypeInfo = () => {
- switch (itemClass) {
- case "Service":
- return {
- icon: "⭐",
- bg: "bg-blue-50 border-blue-200",
- iconBg: "bg-blue-100 text-blue-600",
- label: "Service",
- labelColor: "text-blue-600",
- };
- case "Installation":
- return {
- icon: "🔧",
- bg: "bg-orange-50 border-orange-200",
- iconBg: "bg-orange-100 text-orange-600",
- label: "Installation",
- labelColor: "text-orange-600",
- };
- case "Add-on":
- return {
- icon: "+",
- bg: "bg-green-50 border-green-200",
- iconBg: "bg-green-100 text-green-600",
- label: "Add-on",
- labelColor: "text-green-600",
- };
- case "Activation":
- return {
- icon: "⚡",
- bg: "bg-purple-50 border-purple-200",
- iconBg: "bg-purple-100 text-purple-600",
- label: "Activation",
- labelColor: "text-purple-600",
- };
- default:
- return {
- icon: "📦",
- bg: "bg-gray-50 border-gray-200",
- iconBg: "bg-gray-100 text-gray-600",
- label: itemClass || "Other",
- labelColor: "text-gray-600",
- };
- }
- };
-
- const typeInfo = getItemTypeInfo();
-
- return (
-
-
-
-
- {typeInfo.icon}
-
-
-
-
-
- {item.product.name}
-
-
- {typeInfo.label}
-
-
-
-
- {item.product.billingCycle}
- {item.quantity > 1 && Qty: {item.quantity}}
- {item.product.itemClass && (
-
- {item.product.itemClass}
-
- )}
-
-
-
-
-
- {item.totalPrice && (
-
- ¥{item.totalPrice.toLocaleString()}
-
- )}
-
- {item.product.billingCycle === "Monthly" ? "/month" : "one-time"}
-
-
-
- );
- })}
-
-
- )}
-
- {/* Pricing Summary */}
- {data?.items &&
- data.items.length > 0 &&
- (() => {
- const totals = calculateDetailedTotals(data.items);
-
- return (
-
-
- {totals.monthlyTotal > 0 && (
-
-
- ¥{totals.monthlyTotal.toLocaleString()}
-
-
Monthly Charges
-
- )}
-
- {totals.oneTimeTotal > 0 && (
-
-
- ¥{totals.oneTimeTotal.toLocaleString()}
-
-
One-time Charges
-
- )}
-
-
- {/* Compact Fee Disclaimer */}
-
-
-
⚠️
-
-
Additional fees may apply
-
- Weekend installation (+¥3,000), express setup, or special configuration
- charges may be added. We'll contact you before applying any additional
- fees.
-
-
-
-
+ )}
);
- })()}
+ })()
+ )}
+
+ {/* Combined Service Overview and Products */}
+ {data && (
+
+ {/* Service Header */}
+
+
{getServiceTypeIcon(data.orderType)}
+
+
+ {data.orderType} Service
+
+
+ Order #{data.orderNumber || data.id.slice(-8)} • Placed{" "}
+ {new Date(data.createdDate).toLocaleDateString("en-US", {
+ weekday: "long",
+ month: "long",
+ day: "numeric",
+ year: "numeric",
+ })}
+
+
+
+ {data.items &&
+ data.items.length > 0 &&
+ (() => {
+ const totals = calculateDetailedTotals(data.items);
+
+ return (
+
+
+ {totals.monthlyTotal > 0 && (
+
+
+ ¥{totals.monthlyTotal.toLocaleString()}
+
+
per month
+
+ )}
+
+ {totals.oneTimeTotal > 0 && (
+
+
+ ¥{totals.oneTimeTotal.toLocaleString()}
+
+
one-time
+
+ )}
+
+ {/* Fallback to TotalAmount if no items or calculation fails */}
+ {totals.monthlyTotal === 0 &&
+ totals.oneTimeTotal === 0 &&
+ data.totalAmount && (
+
+
+ ¥{data.totalAmount.toLocaleString()}
+
+
total amount
+
+ )}
+
+
+ );
+ })()}
+
+
+ {/* Services & Products Section */}
+ {data?.items && data.items.length > 0 && (
+
+
Your Services & Products
+
+ {data.items
+ .sort((a, b) => {
+ // Sort: Services first, then Installations, then others
+ const aIsService = a.product.itemClass === "Service";
+ const bIsService = b.product.itemClass === "Service";
+ const aIsInstallation = a.product.itemClass === "Installation";
+ const bIsInstallation = b.product.itemClass === "Installation";
+
+ if (aIsService && !bIsService) return -1;
+ if (!aIsService && bIsService) return 1;
+ if (aIsInstallation && !bIsInstallation) return -1;
+ if (!aIsInstallation && bIsInstallation) return 1;
+ return 0;
+ })
+ .map(item => {
+ // 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:
,
+ 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:
,
+ 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:
,
+ 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:
,
+ bg: "bg-orange-50 border-orange-200",
+ iconBg: "bg-orange-100 text-orange-600",
+ label: itemClass || "Add-on",
+ labelColor: "text-orange-600",
+ };
+ }
+ };
+
+ const typeInfo = getItemTypeInfo();
+
+ return (
+
+
+
+
+ {typeInfo.icon}
+
+
+
+
+
+ {item.product.name}
+
+
+ {typeInfo.label}
+
+
+
+
+ {item.product.billingCycle}
+ {item.quantity > 1 && Qty: {item.quantity}}
+ {item.product.itemClass && (
+
+ {item.product.itemClass}
+
+ )}
+
+
+
+
+
+ {item.totalPrice && (
+
+ ¥{item.totalPrice.toLocaleString()}
+
+ )}
+
+ {item.product.billingCycle === "Monthly" ? "/month" : "one-time"}
+
+
+
+
+ );
+ })}
+
+ {/* Additional fees warning */}
+
+
+
+
+
Additional fees may apply
+
+ Weekend installation (+¥3,000), express setup, or special configuration
+ charges may be added. We will contact you before applying any additional
+ fees.
+
+
+
+
+
+
+ )}
+
+ )}
+
+
{/* Support Contact */}
-
+
Questions about your order? Contact our support team.
-
diff --git a/apps/portal/src/app/orders/page.tsx b/apps/portal/src/app/orders/page.tsx
index 1aab3e63..4f456973 100644
--- a/apps/portal/src/app/orders/page.tsx
+++ b/apps/portal/src/app/orders/page.tsx
@@ -3,7 +3,7 @@
import { useEffect, useState, Suspense } from "react";
import { useRouter, useSearchParams } from "next/navigation";
import { PageLayout } from "@/components/layout/page-layout";
-import { ClipboardDocumentListIcon, CheckCircleIcon } from "@heroicons/react/24/outline";
+import { ClipboardDocumentListIcon, CheckCircleIcon, WifiIcon, DevicePhoneMobileIcon, LockClosedIcon, CubeIcon } from "@heroicons/react/24/outline";
import { StatusPill } from "@/components/ui/status-pill";
import { authenticatedApi } from "@/lib/api";
@@ -92,7 +92,7 @@ export default function OrdersPage() {
color: "text-blue-800",
bgColor: "bg-blue-100",
description: "We're reviewing your order",
- nextAction: "We'll contact you within 1-2 business days",
+ nextAction: "We'll contact you within 1 business day",
};
}
@@ -117,13 +117,13 @@ export default function OrdersPage() {
const getServiceTypeDisplay = (orderType?: string) => {
switch (orderType) {
case "Internet":
- return { icon: "🌐", label: "Internet Service" };
+ return { icon:
, label: "Internet Service" };
case "SIM":
- return { icon: "📱", label: "Mobile Service" };
+ return { icon:
, label: "Mobile Service" };
case "VPN":
- return { icon: "🔒", label: "VPN Service" };
+ return { icon:
, label: "VPN Service" };
default:
- return { icon: "📦", label: "Service" };
+ return { icon:
, label: "Service" };
}
};