refactor: add useDebounce hook and replace manual debounce in admin search

- Create reusable useDebounce<T> hook in frontend/src/hooks/useDebounce.ts
- Replace manual setTimeout/clearTimeout + extra state in AdminAppsPage
  with clean useDebounce(search, 400) pattern
- Removes timer state and handleSearch wrapper function
This commit is contained in:
keyhan
2026-04-08 12:40:25 +03:30
parent ecedf9119e
commit cb9320ee15
3 changed files with 33 additions and 12 deletions
+4 -11
View File
@@ -8,6 +8,7 @@ import { toast } from 'react-toastify';
import type { Application } from '@/types';
import { Search, X, Package, Hexagon, User, Database, Box } from 'lucide-react';
import { useConfirm } from '@/components/confirm-modal';
import { useDebounce } from '@/hooks/useDebounce';
const statusColors: Record<string, string> = {
running: 'badge-green',
@@ -23,15 +24,7 @@ export default function AdminAppsPage() {
const queryClient = useQueryClient();
const confirm = useConfirm();
const [search, setSearch] = useState('');
const [debouncedSearch, setDebouncedSearch] = useState('');
const [timer, setTimer] = useState<NodeJS.Timeout | null>(null);
const handleSearch = (value: string) => {
setSearch(value);
if (timer) clearTimeout(timer);
const t = setTimeout(() => setDebouncedSearch(value), 400);
setTimer(t);
};
const debouncedSearch = useDebounce(search, 400);
const { data: apps = [], isLoading } = useQuery<Application[]>({
queryKey: ['admin-applications', debouncedSearch],
@@ -138,12 +131,12 @@ export default function AdminAppsPage() {
type="text"
placeholder="Search by app name, user name, email, or user ID…"
value={search}
onChange={(e) => handleSearch(e.target.value)}
onChange={(e) => setSearch(e.target.value)}
className="input pl-10 w-full"
/>
{search && (
<button
onClick={() => { setSearch(''); setDebouncedSearch(''); }}
onClick={() => setSearch('')}
className="absolute right-3 top-1/2 -translate-y-1/2 text-gray-400 hover:text-gray-600"
>
<X className="w-4 h-4" />