wulf-pulse/components/mobile/EngagementSearchInput.tsx

49 lines
1.5 KiB
TypeScript

'use client';
/* EngagementSearchInput — phase 07 (ENG-04).
* Purpose: Search input with leading Search icon. Debounced 300ms before emitting onChange.
* Page applies the filter client-side on loaded users (D-21).
* Props: value (controlled string), onChange (debounced callback). */
import { useEffect, useState } from 'react';
import { Search } from 'lucide-react';
import { Input } from '@/components/ui/input';
export interface EngagementSearchInputProps {
value: string;
onChange: (next: string) => void;
}
export function EngagementSearchInput({ value, onChange }: EngagementSearchInputProps) {
// Local immediate state for the input; debounce flushes to onChange
const [local, setLocal] = useState<string>(value);
// Keep local state synced when the parent resets (e.g. "Clear search" CTA on no-matches state)
useEffect(() => {
setLocal(value);
}, [value]);
// 300ms debounce per D-21
useEffect(() => {
if (local === value) return;
const id = setTimeout(() => { onChange(local); }, 300);
return () => clearTimeout(id);
}, [local, value, onChange]);
return (
<div className="relative">
<Search
className="absolute left-3 top-1/2 -translate-y-1/2 h-4 w-4 text-muted-foreground pointer-events-none"
aria-hidden="true"
/>
<Input
type="search"
placeholder="Search by name or email"
aria-label="Search team members by name or email"
className="pl-9"
value={local}
onChange={(e) => setLocal(e.target.value)}
/>
</div>
);
}