diff --git a/admin-spa/src/routes/Customers/Detail.tsx b/admin-spa/src/routes/Customers/Detail.tsx index 9864288..177ea1e 100644 --- a/admin-spa/src/routes/Customers/Detail.tsx +++ b/admin-spa/src/routes/Customers/Detail.tsx @@ -10,6 +10,7 @@ import { ErrorCard } from '@/components/ErrorCard'; import { Skeleton } from '@/components/ui/skeleton'; import { Card } from '@/components/ui/card'; import { Button } from '@/components/ui/button'; +import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs'; import { ArrowLeft, Edit, Mail, Calendar, ShoppingBag, DollarSign, User, MapPin } from 'lucide-react'; import { formatMoney } from '@/lib/currency'; @@ -17,6 +18,7 @@ export default function CustomerDetail() { const { id } = useParams<{ id: string }>(); const navigate = useNavigate(); const customerId = parseInt(id || '0', 10); + const [activeTab, setActiveTab] = React.useState('overview'); // Fetch customer data const customerQuery = useQuery({ @@ -89,9 +91,9 @@ export default function CustomerDetail() { return (
- {/* Customer Info Card */} + {/* Customer Info Header */} -
+
@@ -109,144 +111,206 @@ export default function CustomerDetail() { {customer.role === 'customer' ? __('Member') : __('Guest')}
- - {/* Stats Grid */} -
-
-
- - {__('Total Orders')} -
-
{customer.stats?.total_orders || 0}
-
- -
-
- - {__('Total Spent')} -
-
- {customer.stats?.total_spent ? formatMoney(customer.stats.total_spent) : formatMoney(0)} -
-
- -
-
- - {__('Registered')} -
-
- {new Date(customer.registered).toLocaleDateString('id-ID', { - year: 'numeric', - month: 'long', - day: 'numeric' - })} -
-
-
- {/* Contact & Address Info */} -
- {/* Contact Info */} - -

- - {__('Contact Information')} -

-
-
-
{__('Email')}
-
{customer.email}
-
- {customer.billing?.phone && ( -
-
{__('Phone')}
-
{customer.billing.phone}
-
- )} -
-
+ {/* Tabs */} + + + {__('Overview')} + {__('Orders')} + {__('Address')} + - {/* Billing Address */} - {customer.billing && (customer.billing.address_1 || customer.billing.city) && ( + {/* Overview Tab */} + + {/* Stats Grid */} +
+ +
+ + {__('Total Orders')} +
+
{customer.stats?.total_orders || 0}
+
+ + +
+ + {__('Total Spent')} +
+
+ {customer.stats?.total_spent ? formatMoney(customer.stats.total_spent) : formatMoney(0)} +
+
+ + +
+ + {__('Registered')} +
+
+ {new Date(customer.registered).toLocaleDateString('id-ID', { + year: 'numeric', + month: 'short', + day: 'numeric' + })} +
+
+
+ + {/* Contact Info */}

- - {__('Billing Address')} + + {__('Contact Information')}

-
- {customer.billing.address_1 &&
{customer.billing.address_1}
} - {customer.billing.address_2 &&
{customer.billing.address_2}
} +
- {[customer.billing.city, customer.billing.state, customer.billing.postcode] - .filter(Boolean) - .join(', ')} +
{__('Email')}
+
{customer.email}
- {customer.billing.country &&
{customer.billing.country}
} + {customer.billing?.phone && ( +
+
{__('Phone')}
+
{customer.billing.phone}
+
+ )}
- )} -
+
- {/* Recent Orders */} - -
-

{__('Recent Orders')}

- {orders.length > 0 && ( - - {__('View all orders')} - - )} -
+ {/* Orders Tab */} + + +
+

{__('Order History')}

+
+ {orders.length} {__('orders')} +
+
- {ordersQuery.isLoading ? ( -
- - -
- ) : orders.length === 0 ? ( -
- -

{__('No orders yet')}

-
- ) : ( -
- {orders.slice(0, 10).map((order: any) => ( - -
-
-
- #{order.number} - - {order.status} - + {ordersQuery.isLoading ? ( +
+ + + +
+ ) : orders.length === 0 ? ( +
+ +

{__('No orders yet')}

+

{__('This customer hasn\'t placed any orders')}

+
+ ) : ( +
+ {orders.map((order: any) => ( + +
+
+
+ #{order.number} + + {order.status} + +
+
+ {new Date(order.date_created).toLocaleDateString('id-ID')} +
+
+
+
{formatMoney(parseFloat(order.total || '0'))}
+
+ {order.line_items?.length || 0} {__('items')} +
+
-
- {new Date(order.date_created).toLocaleDateString('id-ID')} + + ))} +
+ )} + + + + {/* Address Tab */} + +
+ {/* Billing Address */} + +

+ + {__('Billing Address')} +

+ {customer.billing && (customer.billing.address_1 || customer.billing.city) ? ( +
+ {customer.billing.first_name && customer.billing.last_name && ( +
+ {customer.billing.first_name} {customer.billing.last_name}
+ )} + {customer.billing.company && ( +
{customer.billing.company}
+ )} + {customer.billing.address_1 &&
{customer.billing.address_1}
} + {customer.billing.address_2 &&
{customer.billing.address_2}
} +
+ {[customer.billing.city, customer.billing.state, customer.billing.postcode] + .filter(Boolean) + .join(', ')}
-
-
{formatMoney(parseFloat(order.total || '0'))}
-
- {order.line_items?.length || 0} {__('items')} + {customer.billing.country &&
{customer.billing.country}
} + {customer.billing.phone && ( +
+
{__('Phone')}
+
{customer.billing.phone}
-
+ )}
- - ))} + ) : ( +
{__('No billing address')}
+ )} + + + {/* Shipping Address */} + +

+ + {__('Shipping Address')} +

+ {customer.shipping && (customer.shipping.address_1 || customer.shipping.city) ? ( +
+ {customer.shipping.first_name && customer.shipping.last_name && ( +
+ {customer.shipping.first_name} {customer.shipping.last_name} +
+ )} + {customer.shipping.company && ( +
{customer.shipping.company}
+ )} + {customer.shipping.address_1 &&
{customer.shipping.address_1}
} + {customer.shipping.address_2 &&
{customer.shipping.address_2}
} +
+ {[customer.shipping.city, customer.shipping.state, customer.shipping.postcode] + .filter(Boolean) + .join(', ')} +
+ {customer.shipping.country &&
{customer.shipping.country}
} +
+ ) : ( +
{__('No shipping address')}
+ )} +
- )} -
+ +
); } diff --git a/includes/Api/OrdersController.php b/includes/Api/OrdersController.php index 02d9fa3..6446b7f 100644 --- a/includes/Api/OrdersController.php +++ b/includes/Api/OrdersController.php @@ -296,6 +296,12 @@ class OrdersController { if ( $search && preg_match( '/^#?(\\d+)$/', $search, $m ) ) { $args['include'] = [ absint( $m[1] ) ]; } + + // Optional customer filter + $customer_id = $req->get_param( 'customer_id' ); + if ( $customer_id ) { + $args['customer_id'] = absint( $customer_id ); + } $result = wc_get_orders( $args );