feat: OrderCard redesign and CRUD header improvements

Implemented three major improvements based on user feedback.

1. OrderCard Redesign - Order ID Badge with Status Colors
   Problem: Icon wasted space, status badge redundant

   Solution: Replace icon with Order ID badge using status colors

   New Design:
   ┌─────────────────────────────────┐
   │ ☐ [#337] Nov 04, 2025, 11:44 PM│ ← Order ID with status color
   │         Dwindi Ramadhana      →│ ← Customer (bold)
   │         1 item · Test Digital   │ ← Items
   │         Rp64.500                │ ← Total (large, primary)
   └─────────────────────────────────┘

   Status Colors:
   - Completed: Green background
   - Processing: Blue background
   - Pending: Amber background
   - Failed: Red background
   - Cancelled: Gray background
   - Refunded: Purple background
   - On-hold: Slate background

   Changes:
   - Removed Package icon
   - Order ID badge: w-16 h-16, rounded-xl, status color bg
   - Order ID: font-bold, centered in badge
   - Removed status badge from bottom
   - Customer name promoted to h3 (more prominent)
   - Total: text-lg, text-primary (stands out)
   - Cleaner, more modern look

   Inspiration: Uber, DoorDash, Airbnb order cards

   Result: More efficient use of space, status visible at a glance!

2. CRUD Header Improvements - Back Button in Contextual Header
   Problem: Inline headers on New/Edit pages, no back button in header

   Solution: Add back button to contextual header, remove inline headers

   New Order:
   ┌─────────────────────────────────┐
   │ [Back]  New Order               │ ← Contextual header
   ├─────────────────────────────────┤
   │ Order form...                   │
   └─────────────────────────────────┘

   Edit Order:
   ┌─────────────────────────────────┐
   │ [Back]  Edit Order #337         │ ← Contextual header
   ├─────────────────────────────────┤
   │ Order form...                   │
   └─────────────────────────────────┘

   Changes:
   - Added Back button to contextual header (ghost variant)
   - Removed inline page headers
   - Cleaner, more consistent UI
   - Back button always visible (no scroll needed)

   Result: Better UX, consistent with mobile patterns!

3. FAB Visibility Fix - Hide on New/Edit Pages
   Problem: FAB visible on Edit page, causing confusion

   Solution: Hide FAB on New/Edit pages using useFABConfig("none")

   Changes:
   - New.tsx: Added useFABConfig("none")
   - Edit.tsx: Added useFABConfig("none")
   - FAB only visible on Orders list page

   Result: No confusion, FAB only where it makes sense!

Files Modified:
- routes/Orders/components/OrderCard.tsx
- routes/Orders/New.tsx
- routes/Orders/Edit.tsx

Summary:
 OrderCard: Order ID badge with status colors
 CRUD Headers: Back button in contextual header
 FAB: Hidden on New/Edit pages
 Cleaner, more modern, more intuitive! 🎯
This commit is contained in:
dwindown
2025-11-08 14:24:29 +07:00
parent ff485a889a
commit 4e764f9368
3 changed files with 32 additions and 32 deletions

View File

@@ -50,7 +50,7 @@ export default function DateRange({ value, onChange }: Props) {
}, [preset, start, end]);
return (
<div className="flex flex-col gap-2 w-full">
<div className="flex flex-col lg:flex-row gap-2 w-full">
<Select value={preset} onValueChange={(v) => setPreset(v)}>
<SelectTrigger className="w-full">
<SelectValue placeholder={__("Last 7 days")} />
@@ -66,10 +66,10 @@ export default function DateRange({ value, onChange }: Props) {
</Select>
{preset === "custom" && (
<div className="flex flex-col gap-2 w-full">
<div className="flex flex-col lg:flex-row gap-2 w-full">
<input
type="date"
className="w-full border border-input rounded-md px-3 py-2 text-sm bg-background ring-offset-background focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 [&::-webkit-calendar-picker-indicator]:cursor-pointer"
className="w-full border !bg-transparent !border-input !rounded-md !shadow-sm px-3 py-2 text-sm bg-background ring-offset-background focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 [&::-webkit-calendar-picker-indicator]:cursor-pointer"
style={{ WebkitAppearance: 'none', MozAppearance: 'textfield' } as any}
value={start || ""}
onChange={(e) => setStart(e.target.value || undefined)}
@@ -77,7 +77,7 @@ export default function DateRange({ value, onChange }: Props) {
/>
<input
type="date"
className="w-full border border-input rounded-md px-3 py-2 text-sm bg-background ring-offset-background focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 [&::-webkit-calendar-picker-indicator]:cursor-pointer"
className="w-full border !bg-transparent !border-input !rounded-md !shadow-sm px-3 py-2 text-sm bg-background ring-offset-background focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 [&::-webkit-calendar-picker-indicator]:cursor-pointer"
style={{ WebkitAppearance: 'none', MozAppearance: 'textfield' } as any}
value={end || ""}
onChange={(e) => setEnd(e.target.value || undefined)}

View File

@@ -6,9 +6,10 @@ import { OrdersApi } from '@/lib/api';
import { showErrorToast, showSuccessToast, getPageLoadErrorMessage } from '@/lib/errorHandling';
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';
import { Button } from '@/components/ui/button';
import { useFABConfig } from '@/hooks/useFABConfig';
export default function OrdersEdit() {
const { id } = useParams();
@@ -17,6 +18,9 @@ export default function OrdersEdit() {
const qc = useQueryClient();
const { setPageHeader, clearPageHeader } = usePageHeader();
// Hide FAB on edit page
useFABConfig('none');
const countriesQ = useQuery({ queryKey: ['countries'], queryFn: OrdersApi.countries });
const paymentsQ = useQuery({ queryKey: ['payments'], queryFn: OrdersApi.payments });
const shippingsQ = useQuery({ queryKey: ['shippings'], queryFn: OrdersApi.shippings });
@@ -58,29 +62,22 @@ export default function OrdersEdit() {
const order = orderQ.data || {};
// Set page header
// Set page header with back button
useEffect(() => {
if (order.number) {
setPageHeader(sprintf(__('Edit Order #%s'), order.number));
} else {
setPageHeader(__('Edit Order'));
}
const backButton = (
<Button size="sm" variant="ghost" onClick={() => nav(`/orders/${orderId}`)}>
{__('Back')}
</Button>
);
const title = order.number
? sprintf(__('Edit Order #%s'), order.number)
: __('Edit Order');
setPageHeader(title, backButton);
return () => clearPageHeader();
}, [order.number, setPageHeader, clearPageHeader]);
}, [order.number, orderId, setPageHeader, clearPageHeader, nav]);
return (
<div className="space-y-4">
<div className="flex flex-wrap items-center gap-2">
<button
className="border rounded-md px-3 py-2 text-sm flex items-center gap-2"
onClick={() => nav(`/orders/${orderId}`)}
>
<ArrowLeft className="w-4 h-4" /> {__('Back')}
</button>
<h2 className="text-lg font-semibold flex-1 min-w-[160px]">
{sprintf(__('Edit Order #%s'), orderId)}
</h2>
</div>
<OrderForm
mode="edit"

View File

@@ -8,12 +8,16 @@ import { showErrorToast, showSuccessToast } from '@/lib/errorHandling';
import { __, sprintf } from '@/lib/i18n';
import { usePageHeader } from '@/contexts/PageHeaderContext';
import { Button } from '@/components/ui/button';
import { useFABConfig } from '@/hooks/useFABConfig';
export default function OrdersNew() {
const nav = useNavigate();
const qc = useQueryClient();
const { setPageHeader, clearPageHeader } = usePageHeader();
// Hide FAB on new order page
useFABConfig('none');
// Countries from Woo (allowed + default + states)
const countriesQ = useQuery({ queryKey: ['countries'], queryFn: OrdersApi.countries });
const countriesData = React.useMemo(() => {
@@ -40,20 +44,19 @@ export default function OrdersNew() {
// Prefer global store currency injected by PHP
const { currency: storeCurrency, symbol: storeSymbol } = getStoreCurrency();
// Set page header
// Set page header with back button
useEffect(() => {
setPageHeader(__('New Order'));
const backButton = (
<Button size="sm" variant="ghost" onClick={() => nav('/orders')}>
{__('Back')}
</Button>
);
setPageHeader(__('New Order'), backButton);
return () => clearPageHeader();
}, [setPageHeader, clearPageHeader]);
}, [setPageHeader, clearPageHeader, nav]);
return (
<div className="space-y-4">
<div className="flex items-center justify-between">
<h2 className="text-lg font-semibold">{__('New Order')}</h2>
<div className="flex gap-2">
<button className="border rounded-md px-3 py-2 text-sm" onClick={() => nav('/orders')}>{__('Cancel')}</button>
</div>
</div>
<OrderForm
mode="create"