diff --git a/admin-spa/src/components/nav/SubmenuBar.tsx b/admin-spa/src/components/nav/SubmenuBar.tsx index cc41394..161d363 100644 --- a/admin-spa/src/components/nav/SubmenuBar.tsx +++ b/admin-spa/src/components/nav/SubmenuBar.tsx @@ -11,13 +11,17 @@ export default function SubmenuBar({ items = [], fullscreen = false, headerVisib // Single source of truth: props.items. No fallbacks, no demos, no path-based defaults if (items.length === 0) return null; + // Hide submenu on mobile for detail/new/edit pages (only show on index) + const isDetailPage = /\/(orders|products|coupons|customers)\/(?:new|\d+(?:\/edit)?)$/.test(pathname); + const hiddenOnMobile = isDetailPage ? 'hidden md:block' : ''; + // Calculate top position based on fullscreen state // Fullscreen: top-0 (no contextual headers, submenu is first element) // Normal: top-[calc(7rem+32px)] (below WP admin bar + menu bar) const topClass = fullscreen ? 'top-0' : 'top-[calc(7rem+32px)]'; return ( -
+
{items.map((it) => { diff --git a/admin-spa/src/routes/Coupons/components/CouponCard.tsx b/admin-spa/src/routes/Coupons/components/CouponCard.tsx new file mode 100644 index 0000000..f2d7b1c --- /dev/null +++ b/admin-spa/src/routes/Coupons/components/CouponCard.tsx @@ -0,0 +1,104 @@ +import React from 'react'; +import { Link } from 'react-router-dom'; +import { ChevronRight, Tag } from 'lucide-react'; +import { __ } from '@/lib/i18n'; +import { Checkbox } from '@/components/ui/checkbox'; +import { Badge } from '@/components/ui/badge'; +import type { Coupon } from '@/lib/api/coupons'; + +interface CouponCardProps { + coupon: Coupon; + selected?: boolean; + onSelect?: (id: number) => void; +} + +export function CouponCard({ coupon, selected, onSelect }: CouponCardProps) { + // Format discount type + const formatDiscountType = (type: string) => { + switch (type) { + case 'percent': + return __('Percentage'); + case 'fixed_cart': + return __('Fixed Cart'); + case 'fixed_product': + return __('Fixed Product'); + default: + return type; + } + }; + + // Format amount + const formatAmount = () => { + if (coupon.discount_type === 'percent') { + return `${coupon.amount}%`; + } + return `Rp${coupon.amount.toLocaleString('id-ID')}`; + }; + + return ( + +
+ {/* Checkbox */} + {onSelect && ( +
{ + e.preventDefault(); + e.stopPropagation(); + onSelect(coupon.id); + }} + > + +
+ )} + + {/* Content */} +
+ {/* Line 1: Code with Badge */} +
+
+ + {coupon.code} +
+ + {formatDiscountType(coupon.discount_type)} + +
+ + {/* Line 2: Description */} + {coupon.description && ( +
+ {coupon.description} +
+ )} + + {/* Line 3: Usage & Expiry */} +
+ + {__('Usage')}: {coupon.usage_count} / {coupon.usage_limit || '∞'} + + {coupon.date_expires && ( + + {__('Expires')}: {new Date(coupon.date_expires).toLocaleDateString('id-ID')} + + )} +
+ + {/* Line 4: Amount */} +
+ {formatAmount()} +
+
+ + {/* Chevron */} + +
+ + ); +} diff --git a/admin-spa/src/routes/Coupons/index.tsx b/admin-spa/src/routes/Coupons/index.tsx index 47dacde..9b6be04 100644 --- a/admin-spa/src/routes/Coupons/index.tsx +++ b/admin-spa/src/routes/Coupons/index.tsx @@ -14,6 +14,7 @@ import { Badge } from '@/components/ui/badge'; import { Trash2, RefreshCw, Edit, Tag, Search, SlidersHorizontal } from 'lucide-react'; import { useFABConfig } from '@/hooks/useFABConfig'; import { CouponFilterSheet } from './components/CouponFilterSheet'; +import { CouponCard } from './components/CouponCard'; export default function CouponsIndex() { const navigate = useNavigate(); @@ -281,7 +282,7 @@ export default function CouponsIndex() {
{/* Mobile Cards */} -
+
{coupons.length === 0 ? ( @@ -306,39 +307,12 @@ export default function CouponsIndex() { ) : ( coupons.map((coupon) => ( - -
-
- toggleSelection(coupon.id)} - /> -
-
{coupon.code}
- {formatDiscountType(coupon.discount_type)} -
-
-
-
{formatAmount(coupon)}
-
-
- {coupon.description && ( -

{coupon.description}

- )} -
-
{__('Usage')}: {coupon.usage_count} / {coupon.usage_limit || '∞'}
- {coupon.date_expires && ( -
{__('Expires')}: {new Date(coupon.date_expires).toLocaleDateString('id-ID')}
- )} -
- -
+ )) )}
diff --git a/admin-spa/src/routes/Customers/index.tsx b/admin-spa/src/routes/Customers/index.tsx index d964097..21c4058 100644 --- a/admin-spa/src/routes/Customers/index.tsx +++ b/admin-spa/src/routes/Customers/index.tsx @@ -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() {
{/* Mobile: Cards */} -
+
{customers.length === 0 ? ( @@ -234,26 +234,55 @@ export default function CustomersIndex() { ) : ( customers.map((customer) => ( - -
- toggleSelection(customer.id)} - /> + +
+ {/* Checkbox */} +
{ + e.preventDefault(); + e.stopPropagation(); + toggleSelection(customer.id); + }} + > + +
+ + {/* Content */}
- + {/* Line 1: Name */} +

{customer.display_name || `${customer.first_name} ${customer.last_name}`} - -

{customer.email}

-
+

+ + {/* Line 2: Email */} +
+ {customer.email} +
+ + {/* Line 3: Stats */} +
{customer.stats?.total_orders || 0} {__('orders')} - - {customer.stats?.total_spent ? formatMoney(customer.stats.total_spent) : '—'} - + {new Date(customer.registered).toLocaleDateString()} +
+ + {/* Line 4: Total Spent */} +
+ {customer.stats?.total_spent ? formatMoney(customer.stats.total_spent) : '—'}
+ + {/* Chevron */} +
- + )) )}
diff --git a/admin-spa/src/routes/Products/components/ProductCard.tsx b/admin-spa/src/routes/Products/components/ProductCard.tsx index 11a0b71..b475c7d 100644 --- a/admin-spa/src/routes/Products/components/ProductCard.tsx +++ b/admin-spa/src/routes/Products/components/ProductCard.tsx @@ -29,7 +29,7 @@ export function ProductCard({ product, selected, onSelect }: ProductCardProps) { return (
diff --git a/admin-spa/src/routes/Products/index.tsx b/admin-spa/src/routes/Products/index.tsx index 75b4f27..5de157d 100644 --- a/admin-spa/src/routes/Products/index.tsx +++ b/admin-spa/src/routes/Products/index.tsx @@ -137,7 +137,7 @@ export default function Products() { const deleteMutation = useMutation({ mutationFn: async (ids: number[]) => { const results = await Promise.allSettled( - ids.map(id => api.del(`/products/${id}`)) + ids.map(id => api.del(`/products/${id}/edit`)) ); const failed = results.filter(r => r.status === 'rejected').length; return { total: ids.length, failed };