Source identity
| Field | Recorded value |
|---|---|
| Source type | GitHub source |
| Original record | Janady13/llms-txt-hub / apps/web/app/members/members-search.tsx |
| Date | Not specified in source |
| Repository | Janady13/llms-txt-hub |
| Revision | e0f4bf978d4df168369c7601d9c5f5ea89e84377 |
| Branch | main |
| Path or archive member | apps/web/app/members/members-search.tsx |
| Original SHA-256 | cac9a2ed4ab7ace4a6d150ef1f53347619eb874601ce6f0941b10098a87e7d4e |
| Git blob SHA | dcad6b38622407eed80928c0acc04312c8f474d3 |
| Reading-copy SHA-256 | cac9a2ed4ab7ace4a6d150ef1f53347619eb874601ce6f0941b10098a87e7d4e |
Recovered source transcript
Download complete source text ↓80 lines · 2.7 KB
'use client'
import { useDebounce } from '@thedaviddias/hooks/use-debounce'
import { Search } from 'lucide-react'
import { usePathname, useRouter, useSearchParams } from 'next/navigation'
import { useCallback, useEffect, useState, useTransition } from 'react'
/**
* Search component for filtering and navigating members
* @returns Member search component with filters
*/
export function MembersSearch() {
const router = useRouter()
const pathname = usePathname()
const searchParams = useSearchParams()
const [isPending, startTransition] = useTransition()
const [searchQuery, setSearchQuery] = useState(searchParams.get('search') || '')
// Debounce the search query
const debouncedSearch = useDebounce(searchQuery, 300)
// Update URL when debounced search changes
useEffect(() => {
const params = new URLSearchParams(searchParams)
if (debouncedSearch) {
params.set('search', debouncedSearch)
} else {
params.delete('search')
}
// Reset page when search changes
params.delete('page')
startTransition(() => {
router.push(`${pathname}?${params.toString()}`)
})
}, [debouncedSearch, pathname, router, searchParams])
const handleSearchChange = useCallback((value: string) => {
setSearchQuery(value)
}, [])
const handleClearSearch = useCallback(() => {
setSearchQuery('')
}, [])
return (
<div className="flex flex-col gap-4 sm:flex-row sm:items-center sm:justify-between">
{/* Search Input */}
<div className="relative flex-1 max-w-sm">
<Search className="absolute left-3 top-1/2 h-4 w-4 -translate-y-1/2 text-muted-foreground" />
<input
type="text"
placeholder="Search members..."
value={searchQuery}
onChange={e => handleSearchChange(e.target.value)}
className="h-10 w-full rounded-md border border-input bg-background px-10 py-2 text-sm ring-offset-background file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50"
/>
{searchQuery && (
<button
type="button"
onClick={handleClearSearch}
className="absolute right-3 top-1/2 -translate-y-1/2 text-muted-foreground hover:text-foreground"
aria-label="Clear search"
>
×
</button>
)}
{isPending && (
<div className="absolute right-10 top-1/2 -translate-y-1/2">
<div className="h-4 w-4 animate-spin rounded-full border-2 border-primary border-t-transparent" />
</div>
)}
</div>
</div>
)
}