From c62fbd943625173c6539ed8ba4baa90206026376 Mon Sep 17 00:00:00 2001 From: dwindown Date: Sat, 8 Nov 2025 13:35:24 +0700 Subject: [PATCH] refine: Polish mobile Orders UI based on feedback MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Addressed all three feedback points from user testing. 1. OrderCard Layout Improvements Problem: Card felt too dense, cramped spacing Changes: - Increased icon size: 10x10 → 12x12 - Increased icon padding: w-10 h-10 → w-12 h-12 - Rounded corners: rounded-lg → rounded-xl - Added shadow-sm for depth - Increased gap between elements: gap-3 → gap-4 - Added space-y-2 for vertical rhythm - Made order number bolder: font-semibold → font-bold - Increased order number size: text-base → text-lg - Made customer name font-medium (was muted) - Made total amount bolder and colored: font-semibold → font-bold text-primary - Increased total size: text-base → text-lg - Better status badge: px-2 py-0.5 → px-2.5 py-1, font-medium → font-semibold - Larger checkbox: default → w-5 h-5 - Centered chevron vertically: mt-2 → self-center Result: More breathing room, better hierarchy, easier to scan 2. FilterBottomSheet Z-Index & Padding Problem: Bottom sheet covered by FAB and bottom nav Changes: - Increased backdrop z-index: z-40 → z-[60] - Increased sheet z-index: z-50 → z-[70] (above FAB z-50) - Made sheet flexbox: added flex flex-col - Made content scrollable: added flex-1 overflow-y-auto - Added bottom padding: pb-24 (space for bottom nav) Result: Sheet now covers FAB, content scrolls, bottom nav visible 3. Contextual Headers for Order Pages Problem: Order Detail, New, Edit pages are actionable but had no headers Solution: Added contextual headers to all three pages Order Detail: - Header: "Order #337" - Actions: [Invoice] [Edit] buttons - Shows order number dynamically - Hides in print mode New Order: - Header: "New Order" - No actions (form has submit) Edit Order: - Header: "Edit Order #337" - No actions (form has submit) - Shows order number dynamically Implementation: - Import usePageHeader - useEffect to set/clear header - Order Detail: Custom action buttons - New/Edit: Simple title only Files Modified: - routes/Orders/components/OrderCard.tsx - routes/Orders/components/FilterBottomSheet.tsx - routes/Orders/Detail.tsx - routes/Orders/New.tsx - routes/Orders/Edit.tsx Result: ✅ Cards feel spacious and scannable ✅ Filter sheet properly layered ✅ Order pages have contextual headers ✅ Consistent mobile UX across all order flows ✅ Professional, polished feel! 🎯 --- admin-spa/src/routes/Orders/Detail.tsx | 32 +++++++++++++++++++ admin-spa/src/routes/Orders/Edit.tsx | 14 +++++++- admin-spa/src/routes/Orders/New.tsx | 11 ++++++- .../Orders/components/FilterBottomSheet.tsx | 6 ++-- .../routes/Orders/components/OrderCard.tsx | 29 +++++++++-------- .../routes/Orders/components/SearchBar.tsx | 1 - 6 files changed, 73 insertions(+), 20 deletions(-) diff --git a/admin-spa/src/routes/Orders/Detail.tsx b/admin-spa/src/routes/Orders/Detail.tsx index 14cd6fc..0fffeae 100644 --- a/admin-spa/src/routes/Orders/Detail.tsx +++ b/admin-spa/src/routes/Orders/Detail.tsx @@ -21,6 +21,7 @@ import { showErrorToast, showSuccessToast, getPageLoadErrorMessage } from '@/lib import { ErrorCard } from '@/components/ErrorCard'; import { InlineLoadingState } from '@/components/LoadingState'; import { __ } from '@/lib/i18n'; +import { usePageHeader } from '@/contexts/PageHeaderContext'; function Money({ value, currency, symbol }: { value?: number; currency?: string; symbol?: string }) { return <>{formatMoney(value, { currency, symbol })}; @@ -44,6 +45,7 @@ export default function OrderShow() { const { id } = useParams<{ id: string }>(); const qc = useQueryClient(); const siteTitle = (window as any).wnw?.siteTitle || 'WooNooW'; + const { setPageHeader, clearPageHeader } = usePageHeader(); const [params, setParams] = useSearchParams(); const mode = params.get('mode'); // undefined | 'label' | 'invoice' @@ -140,6 +142,36 @@ export default function OrderShow() { retryPaymentMutation.mutate(); } + // Set page header with actions + useEffect(() => { + if (!order || isPrintMode) { + clearPageHeader(); + return; + } + + const actions = ( +
+ + + + +
+ ); + + setPageHeader( + order.number ? `${__('Order')} #${order.number}` : __('Order'), + actions + ); + + return () => clearPageHeader(); + }, [order, isPrintMode, id, setPageHeader, clearPageHeader]); + useEffect(() => { if (!isPrintMode || !qrRef.current || !order) return; (async () => { diff --git a/admin-spa/src/routes/Orders/Edit.tsx b/admin-spa/src/routes/Orders/Edit.tsx index a6971ac..329e20e 100644 --- a/admin-spa/src/routes/Orders/Edit.tsx +++ b/admin-spa/src/routes/Orders/Edit.tsx @@ -1,4 +1,4 @@ -import React from 'react'; +import React, { useEffect } from 'react'; import { useParams, useNavigate } from 'react-router-dom'; import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query'; import OrderForm from '@/routes/Orders/partials/OrderForm'; @@ -8,12 +8,14 @@ import { ErrorCard } from '@/components/ErrorCard'; import { LoadingState } from '@/components/LoadingState'; import { ArrowLeft } from 'lucide-react'; import { __, sprintf } from '@/lib/i18n'; +import { usePageHeader } from '@/contexts/PageHeaderContext'; export default function OrdersEdit() { const { id } = useParams(); const orderId = Number(id); const nav = useNavigate(); const qc = useQueryClient(); + const { setPageHeader, clearPageHeader } = usePageHeader(); const countriesQ = useQuery({ queryKey: ['countries'], queryFn: OrdersApi.countries }); const paymentsQ = useQuery({ queryKey: ['payments'], queryFn: OrdersApi.payments }); @@ -56,6 +58,16 @@ export default function OrdersEdit() { const order = orderQ.data || {}; + // Set page header + useEffect(() => { + if (order.number) { + setPageHeader(sprintf(__('Edit Order #%s'), order.number)); + } else { + setPageHeader(__('Edit Order')); + } + return () => clearPageHeader(); + }, [order.number, setPageHeader, clearPageHeader]); + return (
diff --git a/admin-spa/src/routes/Orders/New.tsx b/admin-spa/src/routes/Orders/New.tsx index b55e7fb..894d8ce 100644 --- a/admin-spa/src/routes/Orders/New.tsx +++ b/admin-spa/src/routes/Orders/New.tsx @@ -1,4 +1,4 @@ -import React from 'react'; +import React, { useEffect } from 'react'; import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'; import { OrdersApi } from '@/lib/api'; import { useNavigate } from 'react-router-dom'; @@ -6,10 +6,13 @@ import OrderForm from '@/routes/Orders/partials/OrderForm'; import { getStoreCurrency } from '@/lib/currency'; import { showErrorToast, showSuccessToast } from '@/lib/errorHandling'; import { __, sprintf } from '@/lib/i18n'; +import { usePageHeader } from '@/contexts/PageHeaderContext'; +import { Button } from '@/components/ui/button'; export default function OrdersNew() { const nav = useNavigate(); const qc = useQueryClient(); + const { setPageHeader, clearPageHeader } = usePageHeader(); // Countries from Woo (allowed + default + states) const countriesQ = useQuery({ queryKey: ['countries'], queryFn: OrdersApi.countries }); @@ -37,6 +40,12 @@ export default function OrdersNew() { // Prefer global store currency injected by PHP const { currency: storeCurrency, symbol: storeSymbol } = getStoreCurrency(); + // Set page header + useEffect(() => { + setPageHeader(__('New Order')); + return () => clearPageHeader(); + }, [setPageHeader, clearPageHeader]); + return (
diff --git a/admin-spa/src/routes/Orders/components/FilterBottomSheet.tsx b/admin-spa/src/routes/Orders/components/FilterBottomSheet.tsx index 711431d..7cdffe9 100644 --- a/admin-spa/src/routes/Orders/components/FilterBottomSheet.tsx +++ b/admin-spa/src/routes/Orders/components/FilterBottomSheet.tsx @@ -44,12 +44,12 @@ export function FilterBottomSheet({ <> {/* Backdrop */}
{/* Bottom Sheet */} -
+
{/* Drag Handle */}
@@ -67,7 +67,7 @@ export function FilterBottomSheet({
{/* Content */} -
+
{/* Status Filter */}
diff --git a/admin-spa/src/routes/Orders/components/OrderCard.tsx b/admin-spa/src/routes/Orders/components/OrderCard.tsx index 7dce108..76d4e03 100644 --- a/admin-spa/src/routes/Orders/components/OrderCard.tsx +++ b/admin-spa/src/routes/Orders/components/OrderCard.tsx @@ -29,9 +29,9 @@ export function OrderCard({ order, selected, onSelect, currencyConfig }: OrderCa return ( -
+
{/* Checkbox */} {onSelect && (
)} {/* Icon */} -
- +
+
{/* Content */} -
+
{/* Order Number & Status */} -
-

#{order.number}

- +
+

#{order.number}

+ {order.status || 'unknown'}
{/* Customer */} -
+
{order.customer || __('Guest')}
{/* Items Brief */} {order.items_brief && ( -
+
{order.items_count} {order.items_count === 1 ? __('item') : __('items')} · {order.items_brief}
)} {/* Date & Total */} -
+
{formatRelativeOrDate(order.date_ts)} - + {formatMoney(order.total, { currency: order.currency || currencyConfig.currency, symbol: order.currency_symbol || currencyConfig.symbol, @@ -95,7 +96,7 @@ export function OrderCard({ order, selected, onSelect, currencyConfig }: OrderCa
{/* Chevron */} - +
); diff --git a/admin-spa/src/routes/Orders/components/SearchBar.tsx b/admin-spa/src/routes/Orders/components/SearchBar.tsx index 201bbda..1368070 100644 --- a/admin-spa/src/routes/Orders/components/SearchBar.tsx +++ b/admin-spa/src/routes/Orders/components/SearchBar.tsx @@ -16,7 +16,6 @@ export function SearchBar({ value, onChange, onFilterClick, filterCount = 0 }: S
onChange(e.target.value)} placeholder={__('Search orders...')}