feat: add search to AdminConsulting page

- Add search by client name, email, category, or order ID
- Show result count for filtered data
- Integrate with existing upcoming/past tabs

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
dwindown
2025-12-28 00:08:21 +07:00
parent 3e418759a1
commit 690268362a

View File

@@ -14,7 +14,7 @@ import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs'; import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs';
import { toast } from '@/hooks/use-toast'; import { toast } from '@/hooks/use-toast';
import { formatIDR } from '@/lib/format'; import { formatIDR } from '@/lib/format';
import { Video, Calendar, Clock, User, Link as LinkIcon, ExternalLink, CheckCircle, XCircle, Loader2 } from 'lucide-react'; import { Video, Calendar, Clock, User, Link as LinkIcon, ExternalLink, CheckCircle, XCircle, Loader2, Search } from 'lucide-react';
import { format, parseISO, isToday, isTomorrow, isPast } from 'date-fns'; import { format, parseISO, isToday, isTomorrow, isPast } from 'date-fns';
import { id } from 'date-fns/locale'; import { id } from 'date-fns/locale';
@@ -72,6 +72,7 @@ export default function AdminConsulting() {
const [saving, setSaving] = useState(false); const [saving, setSaving] = useState(false);
const [creatingMeet, setCreatingMeet] = useState(false); const [creatingMeet, setCreatingMeet] = useState(false);
const [activeTab, setActiveTab] = useState('upcoming'); const [activeTab, setActiveTab] = useState('upcoming');
const [searchQuery, setSearchQuery] = useState('');
useEffect(() => { useEffect(() => {
if (!authLoading) { if (!authLoading) {
@@ -282,10 +283,24 @@ export default function AdminConsulting() {
})).sort((a, b) => new Date(a.firstDate).getTime() - new Date(b.firstDate).getTime()); })).sort((a, b) => new Date(a.firstDate).getTime() - new Date(b.firstDate).getTime());
})(); })();
// Filter orders based on search query
const filteredGroupedOrders = groupedOrders.filter(order => {
if (!searchQuery) return true;
const query = searchQuery.toLowerCase();
const firstSlot = order.slots[0];
return (
order.profile?.name?.toLowerCase().includes(query) ||
order.profile?.email?.toLowerCase().includes(query) ||
firstSlot.topic_category?.toLowerCase().includes(query) ||
order.orderId?.toLowerCase().includes(query)
);
});
const today = new Date().toISOString().split('T')[0]; const today = new Date().toISOString().split('T')[0];
const upcomingOrders = groupedOrders.filter(o => o.firstDate >= today && o.slots.some(s => s.status === 'confirmed' || s.status === 'pending_payment')); const upcomingOrders = filteredGroupedOrders.filter(o => o.firstDate >= today && o.slots.some(s => s.status === 'confirmed' || s.status === 'pending_payment'));
const pastOrders = groupedOrders.filter(o => o.firstDate < today || o.slots.every(s => s.status === 'completed' || s.status === 'cancelled')); const pastOrders = filteredGroupedOrders.filter(o => o.firstDate < today || o.slots.every(s => s.status === 'completed' || s.status === 'cancelled'));
const todayOrders = groupedOrders.filter(o => isToday(parseISO(o.firstDate)) && o.slots.some(s => s.status === 'confirmed')); const todayOrders = filteredGroupedOrders.filter(o => isToday(parseISO(o.firstDate)) && o.slots.some(s => s.status === 'confirmed'));
return ( return (
<AppLayout> <AppLayout>
@@ -360,6 +375,24 @@ export default function AdminConsulting() {
</Card> </Card>
</div> </div>
{/* Search */}
<Card className="border-2 border-border mb-6">
<CardContent className="pt-6">
<div className="relative">
<Search className="absolute left-3 top-1/2 -translate-y-1/2 w-4 h-4 text-muted-foreground" />
<Input
placeholder="Cari nama klien, email, kategori, atau order ID..."
value={searchQuery}
onChange={(e) => setSearchQuery(e.target.value)}
className="pl-10 border-2"
/>
</div>
<div className="mt-2 text-sm text-muted-foreground">
Menampilkan {filteredGroupedOrders.length} dari {groupedOrders.length} jadwal konsultasi
</div>
</CardContent>
</Card>
{/* Tabs */} {/* Tabs */}
<Tabs value={activeTab} onValueChange={setActiveTab}> <Tabs value={activeTab} onValueChange={setActiveTab}>
<TabsList className="mb-4"> <TabsList className="mb-4">