mega-plant/components/GlobalSearch.tsx

99 lines
3.5 KiB
TypeScript

"use client";
import { useState, useEffect, useMemo } from "react";
import Link from "next/link";
import { Search } from "lucide-react";
import { useRouter } from "next/navigation";
import { cn } from "@/lib/utils";
type Asset = {
id: string;
code: string;
name: string;
category: string;
supplier: string;
};
export function GlobalSearch({ assets }: { assets: Asset[] }) {
const [query, setQuery] = useState("");
const [open, setOpen] = useState(false);
const router = useRouter();
const matches = useMemo(() => {
const q = query.trim().toLowerCase();
if (q.length < 2) return [];
return assets
.filter(
(a) =>
a.code.toLowerCase().includes(q) ||
a.name.toLowerCase().includes(q) ||
(a.supplier || "").toLowerCase().includes(q)
)
.slice(0, 8);
}, [query, assets]);
return (
<div className="relative flex-1 max-w-md">
<div className="relative">
<Search className="absolute left-3 top-1/2 -translate-y-1/2 w-4 h-4 text-gray-400 dark:text-gray-500 pointer-events-none" />
<input
value={query}
onChange={(e) => {
setQuery(e.target.value);
setOpen(true);
}}
onFocus={() => setOpen(true)}
onBlur={() => setTimeout(() => setOpen(false), 150)}
onKeyDown={(e) => {
if (e.key === "Enter" && matches[0]) {
router.push(`/income?asset=${matches[0].id}`);
setOpen(false);
setQuery("");
}
}}
placeholder="Search plant, supplier, code…"
className="w-full h-9 pl-9 pr-3 bg-gray-100 dark:bg-white/5 border border-transparent rounded-lg text-sm placeholder:text-gray-400 dark:placeholder:text-gray-500 focus:bg-white dark:focus:bg-[#0f1419] focus:border-[#30638E] focus:outline-none transition"
/>
</div>
{open && matches.length > 0 && (
<div className="absolute top-full left-0 right-0 mt-2 bg-white dark:bg-[#131a21] border border-gray-100 dark:border-white/10 rounded-xl shadow-lg overflow-hidden z-50">
{matches.map((a) => (
<Link
key={a.id}
href={`/income?asset=${a.id}`}
onClick={() => {
setOpen(false);
setQuery("");
}}
className={cn(
"flex items-center gap-3 px-4 py-2.5 hover:bg-gray-50 dark:hover:bg-white/5 text-sm border-b border-gray-50 dark:border-white/5 last:border-0"
)}
>
{a.code ? (
<span className="font-mono text-xs px-2 py-0.5 rounded bg-[#00798C]/10 text-[#00798C] font-semibold shrink-0">
{a.code}
</span>
) : (
<span className="text-[10px] px-2 py-0.5 rounded bg-gray-100 dark:bg-white/5 text-gray-400 shrink-0">
no code
</span>
)}
<div className="flex-1 min-w-0">
<div className="font-medium text-[#003D5B] dark:text-white truncate">{a.name}</div>
{a.supplier && (
<div className="text-xs text-gray-500 dark:text-gray-400 truncate">{a.supplier}</div>
)}
</div>
{a.category && (
<span className="text-[10px] uppercase tracking-wider text-gray-400 dark:text-gray-500 shrink-0">
{a.category}
</span>
)}
</Link>
))}
</div>
)}
</div>
);
}