Enhance order processing and address confirmation features
- Added detailed logging for order creation and error handling in OrdersController and OrderOrchestrator. - Introduced address management enhancements in Order DTO to support updated checkout processes. - Improved address confirmation component to handle controlled state and prevent unnecessary updates for Internet orders. - Added validation checks for Internet orders to ensure required service plans and installations are selected. - Enhanced debugging information in Checkout component for better tracking of order submission and address confirmation states.
This commit is contained in:
parent
807d37a729
commit
543afc8a10
@ -78,6 +78,18 @@ export class OrderConfigurations {
|
|||||||
@IsString()
|
@IsString()
|
||||||
portingDateOfBirth?: string;
|
portingDateOfBirth?: string;
|
||||||
|
|
||||||
|
// Address (when address is updated during checkout)
|
||||||
|
@IsOptional()
|
||||||
|
@IsObject()
|
||||||
|
address?: {
|
||||||
|
street?: string | null;
|
||||||
|
streetLine2?: string | null;
|
||||||
|
city?: string | null;
|
||||||
|
state?: string | null;
|
||||||
|
postalCode?: string | null;
|
||||||
|
country?: string | null;
|
||||||
|
};
|
||||||
|
|
||||||
// VPN region is inferred from product VPN_Region__c field, no user input needed
|
// VPN region is inferred from product VPN_Region__c field, no user input needed
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -24,6 +24,7 @@ export class OrdersController {
|
|||||||
userId: req.user?.id,
|
userId: req.user?.id,
|
||||||
orderType: body.orderType,
|
orderType: body.orderType,
|
||||||
skuCount: body.skus?.length || 0,
|
skuCount: body.skus?.length || 0,
|
||||||
|
requestBody: JSON.stringify(body, null, 2),
|
||||||
},
|
},
|
||||||
"Order creation request received"
|
"Order creation request received"
|
||||||
);
|
);
|
||||||
@ -34,8 +35,10 @@ export class OrdersController {
|
|||||||
this.logger.error(
|
this.logger.error(
|
||||||
{
|
{
|
||||||
error: error instanceof Error ? error.message : String(error),
|
error: error instanceof Error ? error.message : String(error),
|
||||||
|
stack: error instanceof Error ? error.stack : undefined,
|
||||||
userId: req.user?.id,
|
userId: req.user?.id,
|
||||||
orderType: body.orderType,
|
orderType: body.orderType,
|
||||||
|
fullRequestBody: JSON.stringify(body, null, 2),
|
||||||
},
|
},
|
||||||
"Order creation failed"
|
"Order creation failed"
|
||||||
);
|
);
|
||||||
|
|||||||
@ -57,13 +57,24 @@ export class OrderOrchestrator {
|
|||||||
// 4) Create Order in Salesforce
|
// 4) Create Order in Salesforce
|
||||||
let created: { id: string };
|
let created: { id: string };
|
||||||
try {
|
try {
|
||||||
|
this.logger.log(
|
||||||
|
{
|
||||||
|
orderFields: JSON.stringify(orderFields, null, 2),
|
||||||
|
fieldsCount: Object.keys(orderFields).length
|
||||||
|
},
|
||||||
|
"About to create Salesforce Order with fields"
|
||||||
|
);
|
||||||
|
|
||||||
created = (await this.sf.sobject("Order").create(orderFields)) as { id: string };
|
created = (await this.sf.sobject("Order").create(orderFields)) as { id: string };
|
||||||
this.logger.log({ orderId: created.id }, "Salesforce Order created successfully");
|
this.logger.log({ orderId: created.id }, "Salesforce Order created successfully");
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
this.logger.error(
|
this.logger.error(
|
||||||
{
|
{
|
||||||
error: error instanceof Error ? error.message : String(error),
|
error: error instanceof Error ? error.message : String(error),
|
||||||
|
errorDetails: error,
|
||||||
|
stack: error instanceof Error ? error.stack : undefined,
|
||||||
orderType: orderFields.Type,
|
orderType: orderFields.Type,
|
||||||
|
orderFields: JSON.stringify(orderFields, null, 2),
|
||||||
},
|
},
|
||||||
"Failed to create Salesforce Order"
|
"Failed to create Salesforce Order"
|
||||||
);
|
);
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import { useState, useEffect, useMemo, Suspense } from "react";
|
import { useState, useEffect, useMemo, useCallback, Suspense } from "react";
|
||||||
import { useSearchParams, useRouter } from "next/navigation";
|
import { useSearchParams, useRouter } from "next/navigation";
|
||||||
import { PageLayout } from "@/components/layout/page-layout";
|
import { PageLayout } from "@/components/layout/page-layout";
|
||||||
import { ShieldCheckIcon, ExclamationTriangleIcon } from "@heroicons/react/24/outline";
|
import { ShieldCheckIcon, ExclamationTriangleIcon } from "@heroicons/react/24/outline";
|
||||||
@ -38,6 +38,7 @@ function CheckoutContent() {
|
|||||||
const [submitting, setSubmitting] = useState(false);
|
const [submitting, setSubmitting] = useState(false);
|
||||||
const [addressConfirmed, setAddressConfirmed] = useState(false);
|
const [addressConfirmed, setAddressConfirmed] = useState(false);
|
||||||
const [confirmedAddress, setConfirmedAddress] = useState<Address | null>(null);
|
const [confirmedAddress, setConfirmedAddress] = useState<Address | null>(null);
|
||||||
|
const [forceUpdate, setForceUpdate] = useState(0);
|
||||||
const [checkoutState, setCheckoutState] = useState<CheckoutState>({
|
const [checkoutState, setCheckoutState] = useState<CheckoutState>({
|
||||||
loading: true,
|
loading: true,
|
||||||
error: null,
|
error: null,
|
||||||
@ -169,6 +170,11 @@ function CheckoutContent() {
|
|||||||
};
|
};
|
||||||
}, [orderType, selections]);
|
}, [orderType, selections]);
|
||||||
|
|
||||||
|
// Debug effect to track addressConfirmed changes
|
||||||
|
useEffect(() => {
|
||||||
|
console.log("🎯 PARENT: addressConfirmed state changed to:", addressConfirmed);
|
||||||
|
}, [addressConfirmed]);
|
||||||
|
|
||||||
const handleSubmitOrder = async () => {
|
const handleSubmitOrder = async () => {
|
||||||
try {
|
try {
|
||||||
setSubmitting(true);
|
setSubmitting(true);
|
||||||
@ -180,6 +186,28 @@ function CheckoutContent() {
|
|||||||
throw new Error("No products selected for order. Please go back and select products.");
|
throw new Error("No products selected for order. Please go back and select products.");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Additional validation for Internet orders
|
||||||
|
if (orderType === "Internet") {
|
||||||
|
const hasServicePlan = checkoutState.orderItems.some(item => item.type === "service");
|
||||||
|
const hasInstallation = checkoutState.orderItems.some(item => item.type === "installation");
|
||||||
|
|
||||||
|
console.log("🔍 Internet order validation:", {
|
||||||
|
hasServicePlan,
|
||||||
|
hasInstallation,
|
||||||
|
orderItems: checkoutState.orderItems,
|
||||||
|
selections: selections
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!hasServicePlan) {
|
||||||
|
throw new Error("Internet service plan is required. Please go back and select a plan.");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Installation is typically required for Internet orders
|
||||||
|
if (!hasInstallation) {
|
||||||
|
console.warn("⚠️ No installation selected for Internet order - this might cause issues");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Send SKUs + configurations - backend resolves product data from SKUs,
|
// Send SKUs + configurations - backend resolves product data from SKUs,
|
||||||
// uses configurations for fields that cannot be inferred
|
// uses configurations for fields that cannot be inferred
|
||||||
const configurations: Record<string, unknown> = {};
|
const configurations: Record<string, unknown> = {};
|
||||||
@ -221,10 +249,29 @@ function CheckoutContent() {
|
|||||||
...(Object.keys(configurations).length > 0 && { configurations }),
|
...(Object.keys(configurations).length > 0 && { configurations }),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
console.log("🚀 Submitting order with data:", JSON.stringify(orderData, null, 2));
|
||||||
|
console.log("🚀 Address confirmed state:", addressConfirmed);
|
||||||
|
console.log("🚀 Confirmed address:", confirmedAddress);
|
||||||
|
console.log("🚀 Order type:", orderType);
|
||||||
|
console.log("🚀 SKUs:", skus);
|
||||||
|
|
||||||
const response = await authenticatedApi.post<{ sfOrderId: string }>("/orders", orderData);
|
const response = await authenticatedApi.post<{ sfOrderId: string }>("/orders", orderData);
|
||||||
router.push(`/orders/${response.sfOrderId}?status=success`);
|
router.push(`/orders/${response.sfOrderId}?status=success`);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Order submission failed:", error);
|
console.error("🚨 Order submission failed:", error);
|
||||||
|
|
||||||
|
// Enhanced error logging for debugging
|
||||||
|
if (error instanceof Error) {
|
||||||
|
console.error("🚨 Error name:", error.name);
|
||||||
|
console.error("🚨 Error message:", error.message);
|
||||||
|
console.error("🚨 Error stack:", error.stack);
|
||||||
|
}
|
||||||
|
|
||||||
|
// If it's an API error, try to get more details
|
||||||
|
if (error && typeof error === 'object' && 'status' in error) {
|
||||||
|
console.error("🚨 HTTP Status:", error.status);
|
||||||
|
console.error("🚨 Error details:", error);
|
||||||
|
}
|
||||||
|
|
||||||
let errorMessage = "Order submission failed";
|
let errorMessage = "Order submission failed";
|
||||||
if (error instanceof Error) {
|
if (error instanceof Error) {
|
||||||
@ -240,18 +287,25 @@ function CheckoutContent() {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleAddressConfirmed = (address?: Address) => {
|
const handleAddressConfirmed = useCallback((address?: Address) => {
|
||||||
console.log("🎯 PARENT: handleAddressConfirmed called with:", address);
|
console.log("🎯 PARENT: handleAddressConfirmed called with:", address);
|
||||||
console.log("🎯 PARENT: Current addressConfirmed state before:", addressConfirmed);
|
console.log("🎯 PARENT: Current addressConfirmed state before:", addressConfirmed);
|
||||||
setAddressConfirmed(true);
|
|
||||||
setConfirmedAddress(address || null);
|
|
||||||
console.log("🎯 PARENT: addressConfirmed state set to true");
|
|
||||||
|
|
||||||
// Force a log after state update (in next tick)
|
console.log("🎯 PARENT: About to call setAddressConfirmed(true)...");
|
||||||
setTimeout(() => {
|
setAddressConfirmed(prev => {
|
||||||
console.log("🎯 PARENT: addressConfirmed state after update:", addressConfirmed);
|
console.log("🎯 PARENT: setAddressConfirmed functional update - prev:", prev, "-> true");
|
||||||
}, 0);
|
return true;
|
||||||
};
|
});
|
||||||
|
console.log("🎯 PARENT: setAddressConfirmed(true) called");
|
||||||
|
|
||||||
|
console.log("🎯 PARENT: About to call setConfirmedAddress...");
|
||||||
|
setConfirmedAddress(address || null);
|
||||||
|
console.log("🎯 PARENT: setConfirmedAddress called");
|
||||||
|
|
||||||
|
// Force a re-render to ensure the UI updates
|
||||||
|
setForceUpdate(prev => prev + 1);
|
||||||
|
console.log("🎯 PARENT: Force update triggered");
|
||||||
|
}, [addressConfirmed]);
|
||||||
|
|
||||||
const handleAddressIncomplete = () => {
|
const handleAddressIncomplete = () => {
|
||||||
setAddressConfirmed(false);
|
setAddressConfirmed(false);
|
||||||
@ -432,11 +486,13 @@ function CheckoutContent() {
|
|||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Debug Info - Remove in production */}
|
{/* Debug Info - Remove in production */}
|
||||||
<div className="bg-gray-100 border rounded-lg p-3 mb-4 text-xs text-gray-600">
|
<div className="bg-yellow-50 border border-yellow-200 rounded-lg p-3 mb-4 text-xs text-gray-700">
|
||||||
<strong>Debug Info:</strong> Address Confirmed: {addressConfirmed ? '✅' : '❌'} ({String(addressConfirmed)}) |
|
<strong>Debug Info:</strong> Address Confirmed: {addressConfirmed ? '✅ TRUE' : '❌ FALSE'} |
|
||||||
Payment Methods: {paymentMethodsLoading ? '⏳ Loading...' : paymentMethodsError ? '❌ Error' : paymentMethods ? `✅ ${paymentMethods.paymentMethods.length} found` : '❌ None'} |
|
Order Type: {orderType} |
|
||||||
Order Items: {checkoutState.orderItems.length} |
|
Order Items: {checkoutState.orderItems.length} |
|
||||||
|
Payment Methods: {paymentMethodsLoading ? '⏳ Loading...' : paymentMethodsError ? '❌ Error' : paymentMethods ? `✅ ${paymentMethods.paymentMethods.length} found` : '❌ None'} |
|
||||||
|
Force Update: {forceUpdate} |
|
||||||
Can Submit: {!(
|
Can Submit: {!(
|
||||||
submitting ||
|
submitting ||
|
||||||
checkoutState.orderItems.length === 0 ||
|
checkoutState.orderItems.length === 0 ||
|
||||||
@ -444,8 +500,7 @@ function CheckoutContent() {
|
|||||||
paymentMethodsLoading ||
|
paymentMethodsLoading ||
|
||||||
!paymentMethods ||
|
!paymentMethods ||
|
||||||
paymentMethods.paymentMethods.length === 0
|
paymentMethods.paymentMethods.length === 0
|
||||||
) ? '✅' : '❌'} |
|
) ? '✅ YES' : '❌ NO'}
|
||||||
Render Time: {new Date().toLocaleTimeString()}
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="flex gap-4">
|
<div className="flex gap-4">
|
||||||
|
|||||||
@ -31,19 +31,38 @@ interface AddressConfirmationProps {
|
|||||||
onAddressConfirmed: (address?: Address) => void;
|
onAddressConfirmed: (address?: Address) => void;
|
||||||
onAddressIncomplete: () => void;
|
onAddressIncomplete: () => void;
|
||||||
orderType?: string; // Add order type to customize behavior
|
orderType?: string; // Add order type to customize behavior
|
||||||
|
// Optional controlled props for parent state management
|
||||||
|
addressConfirmed?: boolean; // If provided, use this instead of internal state
|
||||||
|
onAddressConfirmationChange?: (confirmed: boolean) => void; // Callback for controlled mode
|
||||||
}
|
}
|
||||||
|
|
||||||
export function AddressConfirmation({
|
export function AddressConfirmation({
|
||||||
onAddressConfirmed,
|
onAddressConfirmed,
|
||||||
onAddressIncomplete,
|
onAddressIncomplete,
|
||||||
orderType,
|
orderType,
|
||||||
|
addressConfirmed: controlledAddressConfirmed,
|
||||||
|
onAddressConfirmationChange,
|
||||||
}: AddressConfirmationProps) {
|
}: AddressConfirmationProps) {
|
||||||
const [billingInfo, setBillingInfo] = useState<BillingInfo | null>(null);
|
const [billingInfo, setBillingInfo] = useState<BillingInfo | null>(null);
|
||||||
const [loading, setLoading] = useState(true);
|
const [loading, setLoading] = useState(true);
|
||||||
const [editing, setEditing] = useState(false);
|
const [editing, setEditing] = useState(false);
|
||||||
const [editedAddress, setEditedAddress] = useState<Address | null>(null);
|
const [editedAddress, setEditedAddress] = useState<Address | null>(null);
|
||||||
const [error, setError] = useState<string | null>(null);
|
const [error, setError] = useState<string | null>(null);
|
||||||
const [addressConfirmed, setAddressConfirmed] = useState(false);
|
const [internalAddressConfirmed, setInternalAddressConfirmed] = useState(false);
|
||||||
|
|
||||||
|
// Use controlled prop if provided, otherwise use internal state
|
||||||
|
const addressConfirmed = controlledAddressConfirmed ?? internalAddressConfirmed;
|
||||||
|
const setAddressConfirmed = (value: boolean | ((prev: boolean) => boolean)) => {
|
||||||
|
const newValue = typeof value === 'function' ? value(addressConfirmed) : value;
|
||||||
|
|
||||||
|
if (controlledAddressConfirmed !== undefined && onAddressConfirmationChange) {
|
||||||
|
// Controlled mode: notify parent
|
||||||
|
onAddressConfirmationChange(newValue);
|
||||||
|
} else {
|
||||||
|
// Uncontrolled mode: update internal state
|
||||||
|
setInternalAddressConfirmed(newValue);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const isInternetOrder = orderType === "Internet";
|
const isInternetOrder = orderType === "Internet";
|
||||||
const requiresAddressVerification = isInternetOrder;
|
const requiresAddressVerification = isInternetOrder;
|
||||||
@ -57,20 +76,33 @@ export function AddressConfirmation({
|
|||||||
// Since address is required at signup, it should always be complete
|
// Since address is required at signup, it should always be complete
|
||||||
// But we still need verification for Internet orders
|
// But we still need verification for Internet orders
|
||||||
if (requiresAddressVerification) {
|
if (requiresAddressVerification) {
|
||||||
// For Internet orders, don't auto-confirm - require explicit verification
|
// For Internet orders, only reset confirmation state if not already confirmed
|
||||||
setAddressConfirmed(false);
|
// This prevents clobbering existing confirmation on re-renders/re-fetches
|
||||||
onAddressIncomplete(); // Keep disabled until explicitly confirmed
|
if (!addressConfirmed) {
|
||||||
|
console.log("🏠 Internet order: Setting initial unconfirmed state");
|
||||||
|
setAddressConfirmed(false);
|
||||||
|
onAddressIncomplete(); // Keep disabled until explicitly confirmed
|
||||||
|
} else {
|
||||||
|
console.log("🏠 Internet order: Preserving existing confirmation state");
|
||||||
|
// Address is already confirmed, don't clobber the state
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
// For other order types, auto-confirm since address exists from signup
|
// For other order types, auto-confirm since address exists from signup
|
||||||
onAddressConfirmed(data.address);
|
// Only call parent callback if we're not already confirmed to avoid spam
|
||||||
setAddressConfirmed(true);
|
if (!addressConfirmed) {
|
||||||
|
console.log("🏠 Non-Internet order: Auto-confirming address");
|
||||||
|
onAddressConfirmed(data.address);
|
||||||
|
setAddressConfirmed(true);
|
||||||
|
} else {
|
||||||
|
console.log("🏠 Non-Internet order: Already confirmed, skipping callback");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
setError(err instanceof Error ? err.message : "Failed to load address");
|
setError(err instanceof Error ? err.message : "Failed to load address");
|
||||||
} finally {
|
} finally {
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
}
|
}
|
||||||
}, [requiresAddressVerification, onAddressIncomplete, onAddressConfirmed]);
|
}, [requiresAddressVerification, onAddressIncomplete, onAddressConfirmed, addressConfirmed]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
void fetchBillingInfo();
|
void fetchBillingInfo();
|
||||||
@ -109,11 +141,11 @@ export function AddressConfirmation({
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
setError(null);
|
setError(null);
|
||||||
// Use the edited address for the order (will be flagged as changed)
|
|
||||||
onAddressConfirmed(editedAddress);
|
// UX-FIRST: Update UI immediately
|
||||||
setEditing(false);
|
setEditing(false);
|
||||||
setAddressConfirmed(true);
|
setAddressConfirmed(true);
|
||||||
|
|
||||||
// Update local state to show the new address
|
// Update local state to show the new address
|
||||||
if (billingInfo) {
|
if (billingInfo) {
|
||||||
setBillingInfo({
|
setBillingInfo({
|
||||||
@ -122,25 +154,39 @@ export function AddressConfirmation({
|
|||||||
isComplete: true,
|
isComplete: true,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SIDE-EFFECT SECOND: Use the edited address for the order (will be flagged as changed)
|
||||||
|
onAddressConfirmed(editedAddress);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
setError(err instanceof Error ? err.message : "Failed to update address");
|
setError(err instanceof Error ? err.message : "Failed to update address");
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleConfirmAddress = () => {
|
const handleConfirmAddress = (e: React.MouseEvent<HTMLButtonElement>) => {
|
||||||
|
// Prevent any default behavior and event propagation
|
||||||
|
e.preventDefault();
|
||||||
|
e.stopPropagation();
|
||||||
|
|
||||||
console.log("🏠 CONFIRM ADDRESS CLICKED", {
|
console.log("🏠 CONFIRM ADDRESS CLICKED", {
|
||||||
billingInfo,
|
billingInfo,
|
||||||
hasAddress: !!billingInfo?.address,
|
hasAddress: !!billingInfo?.address,
|
||||||
address: billingInfo?.address
|
address: billingInfo?.address,
|
||||||
|
currentAddressConfirmed: addressConfirmed
|
||||||
});
|
});
|
||||||
|
|
||||||
if (billingInfo?.address) {
|
if (billingInfo?.address) {
|
||||||
console.log("🏠 Calling onAddressConfirmed with:", billingInfo.address);
|
console.log("🏠 UX-First approach: Updating local state immediately for instant UI feedback");
|
||||||
onAddressConfirmed(billingInfo.address);
|
|
||||||
|
// UX-FIRST: Update local state immediately for instant UI response
|
||||||
setAddressConfirmed(true);
|
setAddressConfirmed(true);
|
||||||
console.log("🏠 Address confirmed state set to true");
|
console.log("🏠 ✅ Local addressConfirmed set to true (UI will update immediately)");
|
||||||
|
|
||||||
|
// SIDE-EFFECT SECOND: Notify parent after local state update
|
||||||
|
console.log("🏠 Notifying parent component...");
|
||||||
|
onAddressConfirmed(billingInfo.address);
|
||||||
|
console.log("🏠 ✅ Parent onAddressConfirmed() called with:", billingInfo.address);
|
||||||
} else {
|
} else {
|
||||||
console.log("🏠 No billing info or address available");
|
console.log("🏠 ❌ No billing info or address available");
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -170,6 +216,7 @@ export function AddressConfirmation({
|
|||||||
<h3 className="text-sm font-medium text-red-800">Address Error</h3>
|
<h3 className="text-sm font-medium text-red-800">Address Error</h3>
|
||||||
<p className="text-sm text-red-700 mt-1">{error}</p>
|
<p className="text-sm text-red-700 mt-1">{error}</p>
|
||||||
<button
|
<button
|
||||||
|
type="button"
|
||||||
onClick={() => void fetchBillingInfo()}
|
onClick={() => void fetchBillingInfo()}
|
||||||
className="text-sm text-red-600 hover:text-red-500 font-medium mt-2"
|
className="text-sm text-red-600 hover:text-red-500 font-medium mt-2"
|
||||||
>
|
>
|
||||||
@ -185,6 +232,16 @@ export function AddressConfirmation({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="bg-white border rounded-xl p-6 mb-6">
|
<div className="bg-white border rounded-xl p-6 mb-6">
|
||||||
|
{/* Debug Info - Remove in production */}
|
||||||
|
<div className="bg-blue-50 border border-blue-200 rounded-lg p-3 mb-4 text-xs text-gray-700">
|
||||||
|
<strong>AddressConfirmation Debug:</strong> isInternetOrder: {isInternetOrder ? '✅' : '❌'} |
|
||||||
|
addressConfirmed: {addressConfirmed ? '✅' : '❌'} |
|
||||||
|
controlledMode: {controlledAddressConfirmed !== undefined ? '✅' : '❌'} |
|
||||||
|
billingInfo: {billingInfo ? '✅' : '❌'} |
|
||||||
|
hasAddress: {billingInfo?.address ? '✅' : '❌'} |
|
||||||
|
showConfirmButton: {(isInternetOrder && !addressConfirmed) ? '✅' : '❌'}
|
||||||
|
</div>
|
||||||
|
|
||||||
<div className="flex items-center justify-between mb-4">
|
<div className="flex items-center justify-between mb-4">
|
||||||
<div className="flex items-center space-x-3">
|
<div className="flex items-center space-x-3">
|
||||||
<MapPinIcon className="h-5 w-5 text-blue-600" />
|
<MapPinIcon className="h-5 w-5 text-blue-600" />
|
||||||
@ -198,6 +255,7 @@ export function AddressConfirmation({
|
|||||||
</div>
|
</div>
|
||||||
{billingInfo.isComplete && !editing && (
|
{billingInfo.isComplete && !editing && (
|
||||||
<button
|
<button
|
||||||
|
type="button"
|
||||||
onClick={handleEdit}
|
onClick={handleEdit}
|
||||||
className="flex items-center space-x-2 text-blue-600 hover:text-blue-700 text-sm font-medium"
|
className="flex items-center space-x-2 text-blue-600 hover:text-blue-700 text-sm font-medium"
|
||||||
>
|
>
|
||||||
@ -326,6 +384,7 @@ export function AddressConfirmation({
|
|||||||
|
|
||||||
<div className="flex items-center space-x-3 pt-4">
|
<div className="flex items-center space-x-3 pt-4">
|
||||||
<button
|
<button
|
||||||
|
type="button"
|
||||||
onClick={handleSave}
|
onClick={handleSave}
|
||||||
className="flex items-center space-x-2 bg-blue-600 text-white px-4 py-2 rounded-lg hover:bg-blue-700 transition-colors"
|
className="flex items-center space-x-2 bg-blue-600 text-white px-4 py-2 rounded-lg hover:bg-blue-700 transition-colors"
|
||||||
>
|
>
|
||||||
@ -333,6 +392,7 @@ export function AddressConfirmation({
|
|||||||
<span>Save Address</span>
|
<span>Save Address</span>
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
|
type="button"
|
||||||
onClick={handleCancel}
|
onClick={handleCancel}
|
||||||
className="flex items-center space-x-2 bg-gray-100 text-gray-700 px-4 py-2 rounded-lg hover:bg-gray-200 transition-colors"
|
className="flex items-center space-x-2 bg-gray-100 text-gray-700 px-4 py-2 rounded-lg hover:bg-gray-200 transition-colors"
|
||||||
>
|
>
|
||||||
@ -366,8 +426,9 @@ export function AddressConfirmation({
|
|||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<button
|
<button
|
||||||
|
type="button"
|
||||||
onClick={handleConfirmAddress}
|
onClick={handleConfirmAddress}
|
||||||
className="bg-green-600 text-white px-4 py-2 rounded-lg hover:bg-green-700 transition-colors font-medium"
|
className="bg-green-600 text-white px-4 py-2 rounded-lg hover:bg-green-700 transition-colors font-medium active:bg-green-800 focus:outline-none focus:ring-2 focus:ring-green-500 focus:ring-offset-2"
|
||||||
>
|
>
|
||||||
✓ Confirm Installation Address
|
✓ Confirm Installation Address
|
||||||
</button>
|
</button>
|
||||||
@ -392,6 +453,7 @@ export function AddressConfirmation({
|
|||||||
<MapPinIcon className="h-12 w-12 text-gray-400 mx-auto mb-4" />
|
<MapPinIcon className="h-12 w-12 text-gray-400 mx-auto mb-4" />
|
||||||
<p className="text-gray-600 mb-4">No address on file</p>
|
<p className="text-gray-600 mb-4">No address on file</p>
|
||||||
<button
|
<button
|
||||||
|
type="button"
|
||||||
onClick={handleEdit}
|
onClick={handleEdit}
|
||||||
className="bg-blue-600 text-white px-4 py-2 rounded-lg hover:bg-blue-700 transition-colors"
|
className="bg-blue-600 text-white px-4 py-2 rounded-lg hover:bg-blue-700 transition-colors"
|
||||||
>
|
>
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user