"use client"; import { useEffect, useRef, useState } from "react"; import Link from "next/link"; interface Hit { type: "user" | "trip" | "booking"; id: string; title: string; subtitle: string; href: string; } /** * Search bar global untuk admin sidebar. Debounced 250ms supaya tidak spam * server. Hits dispatch berdasarkan pola input — lihat * `adminSearchService.resolve` di server. */ export function AdminSearchBar() { const [query, setQuery] = useState(""); const [hits, setHits] = useState([]); const [open, setOpen] = useState(false); const [loading, setLoading] = useState(false); const wrapperRef = useRef(null); // Debounced fetch — guard inside async block supaya tidak setState langsung // di effect synchronous (react-hooks/set-state-in-effect). useEffect(() => { const q = query.trim(); const controller = new AbortController(); const timer = setTimeout(() => { if (q.length < 2) { setHits([]); setLoading(false); return; } setLoading(true); fetch(`/api/admin/search?q=${encodeURIComponent(q)}`, { signal: controller.signal, }) .then((res) => (res.ok ? res.json() : { hits: [] })) .then((json: { hits: Hit[] }) => { setHits(json.hits ?? []); }) .catch(() => setHits([])) .finally(() => setLoading(false)); }, 250); return () => { clearTimeout(timer); controller.abort(); }; }, [query]); // Close dropdown on outside click useEffect(() => { function onClick(e: MouseEvent) { if ( wrapperRef.current && !wrapperRef.current.contains(e.target as Node) ) { setOpen(false); } } document.addEventListener("mousedown", onClick); return () => document.removeEventListener("mousedown", onClick); }, []); return (
{ setQuery(e.target.value); setOpen(true); }} onFocus={() => setOpen(true)} placeholder="Cari email, ID, order_id, judul..." className="w-full rounded-lg border border-neutral-200 bg-neutral-50 px-3 py-1.5 text-xs text-neutral-800 placeholder:text-neutral-400 focus:border-primary-400 focus:bg-white" /> {open && query.trim().length >= 2 && (
{loading && (

Mencari...

)} {!loading && hits.length === 0 && (

Tidak ada hasil.

)} {!loading && hits.length > 0 && (
    {hits.map((h) => (
  • { setOpen(false); setQuery(""); }} className="flex items-center gap-2 px-3 py-2 hover:bg-neutral-50" > {h.type}

    {h.title}

    {h.subtitle}

  • ))}
)}
)}
); }