diff --git a/admin-spa/src/components/VerticalTabForm.tsx b/admin-spa/src/components/VerticalTabForm.tsx index 9e33e4e..a632ebc 100644 --- a/admin-spa/src/components/VerticalTabForm.tsx +++ b/admin-spa/src/components/VerticalTabForm.tsx @@ -92,13 +92,15 @@ export function VerticalTabForm({ tabs, children, className }: VerticalTabFormPr {/* Content Area */}
{React.Children.map(children, (child) => { if (React.isValidElement(child) && child.props['data-section-id']) { const sectionId = child.props['data-section-id']; + const isActive = sectionId === activeTab; return React.cloneElement(child as React.ReactElement, { ref: (el: HTMLElement) => registerSection(sectionId, el), + className: isActive ? '' : 'hidden', }); } return child; diff --git a/admin-spa/src/routes/Coupons/Edit.tsx b/admin-spa/src/routes/Coupons/Edit.tsx index 9ad5261..6757668 100644 --- a/admin-spa/src/routes/Coupons/Edit.tsx +++ b/admin-spa/src/routes/Coupons/Edit.tsx @@ -89,7 +89,7 @@ export default function CouponEdit() { } return ( -
+
+
{ diff --git a/admin-spa/src/routes/Coupons/components/CouponFilterSheet.tsx b/admin-spa/src/routes/Coupons/components/CouponFilterSheet.tsx new file mode 100644 index 0000000..61e9923 --- /dev/null +++ b/admin-spa/src/routes/Coupons/components/CouponFilterSheet.tsx @@ -0,0 +1,80 @@ +import React, { useState } from 'react'; +import { __ } from '@/lib/i18n'; +import { Sheet, SheetContent, SheetHeader, SheetTitle } from '@/components/ui/sheet'; +import { Button } from '@/components/ui/button'; +import { Label } from '@/components/ui/label'; +import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select'; + +interface CouponFilterSheetProps { + open: boolean; + onClose: () => void; + filters: { + discount_type: string; + }; + onFiltersChange: (filters: { discount_type: string }) => void; + onReset: () => void; +} + +export function CouponFilterSheet({ + open, + onClose, + filters, + onFiltersChange, + onReset, +}: CouponFilterSheetProps) { + const [localFilters, setLocalFilters] = useState(filters); + + const handleApply = () => { + onFiltersChange(localFilters); + onClose(); + }; + + const handleReset = () => { + setLocalFilters({ discount_type: '' }); + onReset(); + onClose(); + }; + + return ( + + + + {__('Filter Coupons')} + + +
+ {/* Discount Type */} +
+ + +
+
+ + {/* Actions */} +
+ + +
+
+
+ ); +} diff --git a/admin-spa/src/routes/Coupons/index.tsx b/admin-spa/src/routes/Coupons/index.tsx index 0dc9ad1..40ee722 100644 --- a/admin-spa/src/routes/Coupons/index.tsx +++ b/admin-spa/src/routes/Coupons/index.tsx @@ -11,8 +11,9 @@ import { Input } from '@/components/ui/input'; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select'; import { Checkbox } from '@/components/ui/checkbox'; import { Badge } from '@/components/ui/badge'; -import { Trash2, RefreshCw, Edit, Tag } from 'lucide-react'; +import { Trash2, RefreshCw, Edit, Tag, Search, SlidersHorizontal } from 'lucide-react'; import { useFABConfig } from '@/hooks/useFABConfig'; +import { CouponFilterSheet } from './components/CouponFilterSheet'; export default function CouponsIndex() { const navigate = useNavigate(); @@ -21,9 +22,13 @@ export default function CouponsIndex() { const [search, setSearch] = useState(''); const [discountType, setDiscountType] = useState(''); const [selectedIds, setSelectedIds] = useState([]); + const [filterSheetOpen, setFilterSheetOpen] = useState(false); // Configure FAB to navigate to new coupon page - useFABConfig('navigate', '/coupons/new'); + useFABConfig('coupons'); + + // Count active filters + const activeFiltersCount = discountType && discountType !== 'all' ? 1 : 0; // Fetch coupons const { data, isLoading, isError, error, refetch } = useQuery({ @@ -110,69 +115,101 @@ export default function CouponsIndex() { const hasActiveFilters = search || (discountType && discountType !== 'all'); return ( -
- {/* Toolbar */} -
- {/* Left: Bulk Actions */} -
- {/* Delete - Show only when items selected */} - {selectedIds.length > 0 && ( - - )} +
+ {/* Mobile: Search + Filter */} +
+
+ {/* Search Input */} +
+ + setSearch(e.target.value)} + placeholder={__('Search coupons...')} + className="w-full pl-10 pr-4 py-2.5 rounded-lg border border-border bg-background text-sm focus:outline-none focus:ring-2 focus:ring-primary focus:border-transparent" + /> +
- {/* Refresh - Always visible (REQUIRED per SOP) */} + {/* Filter Button */}
+
- {/* Right: Filters */} -
- {/* Discount Type Filter */} - + {/* Desktop Toolbar */} +
+
+ + {/* Left: Bulk Actions */} +
+ {/* Delete - Show only when items selected */} + {selectedIds.length > 0 && ( + + )} - {/* Search */} - setSearch(e.target.value)} - className="w-[200px]" - /> - - {/* Reset Filters - Text link style per SOP */} - {hasActiveFilters && ( + {/* Refresh - Always visible (REQUIRED per SOP) */} - )} +
+ + {/* Right: Filters */} +
+ {/* Discount Type Filter */} + + + {/* Search */} + setSearch(e.target.value)} + className="w-[200px]" + /> + + {/* Reset Filters - Text link style per SOP */} + {hasActiveFilters && ( + + )} +
@@ -328,6 +365,15 @@ export default function CouponsIndex() {
)} + + {/* Mobile Filter Sheet */} + setFilterSheetOpen(false)} + filters={{ discount_type: discountType }} + onFiltersChange={(filters) => setDiscountType(filters.discount_type)} + onReset={() => setDiscountType('')} + />
); }