Files
tfm_ainventory/frontend/hooks/useItemSearch.ts

90 lines
2.7 KiB
TypeScript

'use client';
import { useState, useCallback } from 'react';
export interface SearchResult {
category?: string;
type?: string;
description?: string;
notes?: string;
}
export interface SearchState {
isSearching: boolean;
searchError: string | null;
searchResults: SearchResult | null;
searchStatus: 'idle' | 'searching' | 'success' | 'timeout' | 'error' | 'no_results' | 'skipped';
retryCount: number;
}
export function useItemSearch(options: { maxRetries?: number; timeout?: number } = {}) {
const { maxRetries = 2, timeout = 30000 } = options;
const [state, setState] = useState<SearchState>({
isSearching: false,
searchError: null,
searchResults: null,
searchStatus: 'idle',
retryCount: 0,
});
const performSearch = useCallback(
async (partNumber: string, category: string): Promise<SearchResult | null> => {
if (!partNumber || !category) {
setState(prev => ({ ...prev, searchStatus: 'skipped', searchError: null }));
return null;
}
setState(prev => ({ ...prev, isSearching: true, searchStatus: 'searching', searchError: null, retryCount: 0 }));
try {
const controller = new AbortController();
const timeoutId = setTimeout(() => controller.abort(), timeout);
const response = await fetch('/api/onboarding/search', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ partNumber, category }),
signal: controller.signal,
});
clearTimeout(timeoutId);
if (!response.ok) throw new Error(`Search failed: ${response.status}`);
const results = await response.json();
setState(prev => ({ ...prev, isSearching: false, searchStatus: 'success', searchResults: results }));
return results;
} catch (error) {
const errorMsg = error instanceof Error ? error.message : 'Search failed';
setState(prev => ({
...prev,
isSearching: false,
searchStatus: errorMsg.includes('abort') ? 'timeout' : 'error',
searchError: errorMsg,
}));
return null;
}
},
[timeout]
);
const retrySearch = useCallback(
async (partNumber: string, category: string) => {
if (state.retryCount >= maxRetries) {
setState(prev => ({ ...prev, searchStatus: 'error' }));
return null;
}
setState(prev => ({ ...prev, retryCount: prev.retryCount + 1 }));
return performSearch(partNumber, category);
},
[state.retryCount, maxRetries, performSearch]
);
const skipSearch = useCallback(() => {
setState(prev => ({ ...prev, searchStatus: 'skipped', isSearching: false }));
}, []);
return { state, performSearch, retrySearch, skipSearch };
}