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:
@@ -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)}
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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"
|
||||
|
||||
Reference in New Issue
Block a user