feat: Implement smart back navigation with fallback across all detail/edit pages

Implemented context-aware back button that respects user's navigation path:

Pattern:
```typescript
const handleBack = () => {
  if (window.history.state?.idx > 0) {
    navigate(-1); // Go back in history
  } else {
    navigate('/fallback'); // Safe fallback
  }
};
```

Updated Pages:
 Orders/Detail.tsx → Fallback: /orders
 Orders/Edit.tsx → Fallback: /orders/:id
 Customers/Detail.tsx → Fallback: /customers
 Customers/Edit.tsx → Fallback: /customers
 Products/Edit.tsx → Fallback: /products
 Coupons/Edit.tsx → Fallback: /coupons

User Flow Examples:

1. Normal Navigation (History Available):
   Customers Index → Customer Detail → Orders Tab → Order Detail
   → Click Back → Returns to Customer Detail 

2. Direct Access (No History):
   User opens /orders/360 directly
   → Click Back → Goes to /orders (fallback) 

3. New Tab (No History):
   User opens order in new tab
   → Click Back → Goes to /orders (fallback) 

4. Page Refresh (History Cleared):
   User refreshes page
   → Click Back → Goes to fallback 

Benefits:
 Respects user's navigation path when possible
 Never breaks or leaves the app
 Predictable behavior in all scenarios
 Professional UX (like Gmail, Shopify, etc.)
 Works with deep links and bookmarks

Technical:
- Uses window.history.state.idx to detect history
- Falls back to safe default when no history
- Consistent pattern across all pages
- No URL parameters needed

Result: Back button now works intelligently based on context!
This commit is contained in:
dwindown
2025-11-21 10:12:26 +07:00
parent bc4b64fd2f
commit c9e036217e
6 changed files with 60 additions and 6 deletions

View File

@@ -43,11 +43,20 @@ export default function CouponEdit() {
}, },
}); });
// Smart back handler: go back in history if available, otherwise fallback to /coupons
const handleBack = () => {
if (window.history.state?.idx > 0) {
navigate(-1); // Go back in history
} else {
navigate('/coupons'); // Fallback to coupons index
}
};
// Set contextual header // Set contextual header
useEffect(() => { useEffect(() => {
const actions = ( const actions = (
<div className="flex gap-2"> <div className="flex gap-2">
<Button size="sm" variant="ghost" onClick={() => navigate('/coupons')}> <Button size="sm" variant="ghost" onClick={handleBack}>
{__('Back')} {__('Back')}
</Button> </Button>
<Button <Button

View File

@@ -37,6 +37,15 @@ export default function CustomerDetail() {
const orders = ordersQuery.data?.rows || []; const orders = ordersQuery.data?.rows || [];
const { setPageHeader, clearPageHeader } = usePageHeader(); const { setPageHeader, clearPageHeader } = usePageHeader();
// Smart back handler: go back in history if available, otherwise fallback to /customers
const handleBack = () => {
if (window.history.state?.idx > 0) {
navigate(-1); // Go back in history
} else {
navigate('/customers'); // Fallback to customers index
}
};
// Page header // Page header
React.useEffect(() => { React.useEffect(() => {
if (!customer) { if (!customer) {
@@ -46,7 +55,7 @@ export default function CustomerDetail() {
const actions = ( const actions = (
<div className="flex gap-2"> <div className="flex gap-2">
<Button size="sm" variant="ghost" onClick={() => navigate('/customers')}> <Button size="sm" variant="ghost" onClick={handleBack}>
{__('Back')} {__('Back')}
</Button> </Button>
<Button size="sm" onClick={() => navigate(`/customers/${customerId}/edit`)}> <Button size="sm" onClick={() => navigate(`/customers/${customerId}/edit`)}>

View File

@@ -46,11 +46,20 @@ export default function CustomerEdit() {
await updateMutation.mutateAsync(data); await updateMutation.mutateAsync(data);
}; };
// Smart back handler: go back in history if available, otherwise fallback to /customers
const handleBack = () => {
if (window.history.state?.idx > 0) {
navigate(-1); // Go back in history
} else {
navigate('/customers'); // Fallback to customers index
}
};
// Set page header with back button and save button // Set page header with back button and save button
useEffect(() => { useEffect(() => {
const actions = ( const actions = (
<div className="flex gap-2"> <div className="flex gap-2">
<Button size="sm" variant="ghost" onClick={() => navigate('/customers')}> <Button size="sm" variant="ghost" onClick={handleBack}>
{__('Back')} {__('Back')}
</Button> </Button>
<Button <Button

View File

@@ -143,6 +143,15 @@ export default function OrderShow() {
retryPaymentMutation.mutate(); retryPaymentMutation.mutate();
} }
// Smart back handler: go back in history if available, otherwise fallback to /orders
const handleBack = () => {
if (window.history.state?.idx > 0) {
nav(-1); // Go back in history
} else {
nav('/orders'); // Fallback to orders index
}
};
// Set contextual header with Back button and Edit action // Set contextual header with Back button and Edit action
useEffect(() => { useEffect(() => {
if (!order || isPrintMode) { if (!order || isPrintMode) {
@@ -152,7 +161,7 @@ export default function OrderShow() {
const actions = ( const actions = (
<div className="flex gap-2"> <div className="flex gap-2">
<Button size="sm" variant="ghost" onClick={() => nav('/orders')}> <Button size="sm" variant="ghost" onClick={handleBack}>
{__('Back')} {__('Back')}
</Button> </Button>
<Link to={`/orders/${id}/edit`}> <Link to={`/orders/${id}/edit`}>

View File

@@ -60,11 +60,20 @@ export default function OrdersEdit() {
} }
}, [order.meta]); }, [order.meta]);
// Smart back handler: go back in history if available, otherwise fallback to order detail
const handleBack = () => {
if (window.history.state?.idx > 0) {
nav(-1); // Go back in history
} else {
nav(`/orders/${orderId}`); // Fallback to order detail
}
};
// Set page header with back button and save button // Set page header with back button and save button
useEffect(() => { useEffect(() => {
const actions = ( const actions = (
<div className="flex gap-2"> <div className="flex gap-2">
<Button size="sm" variant="ghost" onClick={() => nav(`/orders/${orderId}`)}> <Button size="sm" variant="ghost" onClick={handleBack}>
{__('Back')} {__('Back')}
</Button> </Button>
<Button <Button

View File

@@ -66,11 +66,20 @@ export default function ProductEdit() {
} }
}, [productQ.data?.meta]); }, [productQ.data?.meta]);
// Smart back handler: go back in history if available, otherwise fallback to /products
const handleBack = () => {
if (window.history.state?.idx > 0) {
navigate(-1); // Go back in history
} else {
navigate('/products'); // Fallback to products index
}
};
// Set page header with back button and save button // Set page header with back button and save button
useEffect(() => { useEffect(() => {
const actions = ( const actions = (
<div className="flex gap-2"> <div className="flex gap-2">
<Button size="sm" variant="ghost" onClick={() => navigate('/products')}> <Button size="sm" variant="ghost" onClick={handleBack}>
{__('Back')} {__('Back')}
</Button> </Button>
<Button <Button