feat(ui): Make cards linkable and hide submenu on detail pages

Improved mobile UX matching Orders/Products pattern

Issue 1: Coupons and Customers cards not linkable
 Cards had separate checkbox and edit button
 Inconsistent with Orders/Products beautiful card design
 Less intuitive UX (extra tap required)

Issue 2: Submenu showing on detail/new/edit pages
 Submenu tabs visible on mobile detail/new/edit pages
 Distracting and annoying (user feedback)
 Redundant (page has own tabs + back button)

Changes Made:

1. Created CouponCard Component:
 Linkable card matching OrderCard/ProductCard pattern
 Whole card is tappable (better mobile UX)
 Checkbox with stopPropagation for selection
 Chevron icon indicating it's tappable
 Beautiful layout: Badge + Description + Usage + Amount
 Active scale animation on tap
 Hover effects

2. Updated Coupons/index.tsx:
 Replaced old card structure with CouponCard
 Fixed desktop edit link: /coupons/${id} → /coupons/${id}/edit
 Changed spacing: space-y-2 → space-y-3 (consistent with Orders)
 Cleaner, more maintainable code

3. Updated Customers/index.tsx:
 Made cards linkable (whole card is Link)
 Added ChevronRight icon
 Checkbox with stopPropagation
 Better layout: Name + Email + Stats + Total Spent
 Changed spacing: space-y-2 → space-y-3
 Matches Orders/Products card design

4. Updated SubmenuBar.tsx:
 Hide on mobile for detail/new/edit pages
 Show on desktop (still useful for navigation)
 Regex pattern: /\/(orders|products|coupons|customers)\/(?:new|\d+(?:\/edit)?)$/
 Applied via: hidden md:block class

Card Pattern Comparison:

Before (Coupons/Customers):

After (All modules):

Submenu Behavior:

Mobile:
- Index pages:  Show submenu [All | New]
- Detail/New/Edit:  Hide submenu (has own tabs + back button)

Desktop:
- All pages:  Show submenu (useful for quick navigation)

Benefits:
 Consistent UX across all modules
 Better mobile experience (fewer taps)
 Less visual clutter on detail pages
 Cleaner, more intuitive navigation
 Matches industry standards (Shopify, WooCommerce)

Result: Mobile UX now matches the beautiful Orders/Products design!
This commit is contained in:
dwindown
2025-11-20 23:34:37 +07:00
parent fe63e08239
commit 97e24ae408
6 changed files with 165 additions and 54 deletions

View File

@@ -11,7 +11,7 @@ import { Button } from '@/components/ui/button';
import { Card } from '@/components/ui/card';
import { ErrorCard } from '@/components/ErrorCard';
import { Skeleton } from '@/components/ui/skeleton';
import { RefreshCw, Trash2, Search, User } from 'lucide-react';
import { RefreshCw, Trash2, Search, User, ChevronRight } from 'lucide-react';
import { formatMoney } from '@/lib/currency';
export default function CustomersIndex() {
@@ -226,7 +226,7 @@ export default function CustomersIndex() {
</div>
{/* Mobile: Cards */}
<div className="md:hidden space-y-2">
<div className="md:hidden space-y-3">
{customers.length === 0 ? (
<Card className="p-8 text-center text-muted-foreground">
<User className="w-12 h-12 mx-auto mb-2 opacity-50" />
@@ -234,26 +234,55 @@ export default function CustomersIndex() {
</Card>
) : (
customers.map((customer) => (
<Card key={customer.id} className="p-4">
<div className="flex items-start gap-3">
<Checkbox
checked={selectedIds.includes(customer.id)}
onCheckedChange={() => toggleSelection(customer.id)}
/>
<Link
key={customer.id}
to={`/customers/${customer.id}/edit`}
className="block bg-card border border-border rounded-xl p-3 hover:bg-accent/50 transition-colors active:scale-[0.98] active:transition-transform shadow-sm"
>
<div className="flex items-center gap-3">
{/* Checkbox */}
<div
onClick={(e) => {
e.preventDefault();
e.stopPropagation();
toggleSelection(customer.id);
}}
>
<Checkbox
checked={selectedIds.includes(customer.id)}
aria-label={__('Select customer')}
className="w-5 h-5"
/>
</div>
{/* Content */}
<div className="flex-1 min-w-0">
<Link to={`/customers/${customer.id}/edit`} className="font-medium hover:underline block">
{/* Line 1: Name */}
<h3 className="font-bold text-base leading-tight mb-1">
{customer.display_name || `${customer.first_name} ${customer.last_name}`}
</Link>
<p className="text-sm text-muted-foreground truncate">{customer.email}</p>
<div className="flex gap-4 mt-2 text-sm">
</h3>
{/* Line 2: Email */}
<div className="text-sm text-muted-foreground truncate mb-2">
{customer.email}
</div>
{/* Line 3: Stats */}
<div className="flex items-center gap-3 text-xs text-muted-foreground mb-1">
<span>{customer.stats?.total_orders || 0} {__('orders')}</span>
<span className="font-medium">
{customer.stats?.total_spent ? formatMoney(customer.stats.total_spent) : '—'}
</span>
<span>{new Date(customer.registered).toLocaleDateString()}</span>
</div>
{/* Line 4: Total Spent */}
<div className="font-bold text-lg tabular-nums text-primary">
{customer.stats?.total_spent ? formatMoney(customer.stats.total_spent) : '—'}
</div>
</div>
{/* Chevron */}
<ChevronRight className="w-5 h-5 text-muted-foreground flex-shrink-0" />
</div>
</Card>
</Link>
))
)}
</div>