import React, { useState, useRef, useCallback } from 'react'; import { useTranslation } from 'react-i18next'; import { searchAccommodation, AccommodationSearchItem, AutocompleteRequest } from '../../utils/api'; import { CrawlingResponse } from '../../types/api'; type SearchType = 'url' | 'name'; // 환경변수에서 테스트 모드 확인 const isTestPage = import.meta.env.VITE_IS_TESTPAGE === 'true'; interface UrlInputContentProps { onAnalyze: (value: string, type?: SearchType) => void; onAutocomplete?: (data: AutocompleteRequest) => void; onTestData?: (data: CrawlingResponse) => void; error: string | null; } const UrlInputContent: React.FC = ({ onAnalyze, onAutocomplete, onTestData, error }) => { const { t, i18n } = useTranslation(); const [inputValue, setInputValue] = useState(''); const [searchType, setSearchType] = useState('url'); const [isDropdownOpen, setIsDropdownOpen] = useState(false); const [autocompleteResults, setAutocompleteResults] = useState([]); const [isAutocompleteLoading, setIsAutocompleteLoading] = useState(false); const [showAutocomplete, setShowAutocomplete] = useState(false); const [selectedItem, setSelectedItem] = useState(null); const [isLoadingTest, setIsLoadingTest] = useState(false); // 테스트 데이터 로드 핸들러 const handleTestData = async () => { if (!onTestData) return; setIsLoadingTest(true); try { const jsonFile = i18n.language === 'en' ? '/example_analysis_en.json' : '/example_analysis.json'; const response = await fetch(jsonFile); const data: CrawlingResponse = await response.json(); onTestData(data); } catch (err) { console.error('테스트 데이터 로드 실패:', err); } finally { setIsLoadingTest(false); } }; const [highlightedIndex, setHighlightedIndex] = useState(-1); const debounceRef = useRef(null); const autocompleteRef = useRef(null); const searchTypeOptions = [ { value: 'url' as SearchType, label: 'URL' }, { value: 'name' as SearchType, label: t('urlInput.searchTypeBusinessName') }, ]; const getPlaceholder = () => { return searchType === 'url' ? 'https://www.castad.com' : t('urlInput.placeholderBusinessName'); }; const getGuideText = () => { return searchType === 'url' ? t('urlInput.guideUrl') : t('urlInput.guideBusinessName'); }; // 업체명 검색 시 자동완성 (디바운스 적용) const handleAutocompleteSearch = useCallback(async (query: string) => { if (!query.trim() || searchType !== 'name') { setAutocompleteResults([]); setShowAutocomplete(false); return; } setIsAutocompleteLoading(true); try { const response = await searchAccommodation(query); setAutocompleteResults(response.items || []); setShowAutocomplete(response.items && response.items.length > 0); } catch (error) { console.error('자동완성 검색 오류:', error); setAutocompleteResults([]); setShowAutocomplete(false); } finally { setIsAutocompleteLoading(false); } }, [searchType]); // 자동완성 항목 선택 - 업체 정보 저장 const handleSelectAutocomplete = (item: AccommodationSearchItem) => { setInputValue(item.title.replace(/<[^>]*>/g, '')); // HTML 태그 제거 setSelectedItem(item); // 선택된 업체 정보 저장 setShowAutocomplete(false); setAutocompleteResults([]); setHighlightedIndex(-1); }; // 키보드 네비게이션 핸들러 const handleKeyDown = (e: React.KeyboardEvent) => { if (!showAutocomplete || autocompleteResults.length === 0) return; switch (e.key) { case 'ArrowDown': e.preventDefault(); setHighlightedIndex(prev => prev < autocompleteResults.length - 1 ? prev + 1 : 0 ); break; case 'ArrowUp': e.preventDefault(); setHighlightedIndex(prev => prev > 0 ? prev - 1 : autocompleteResults.length - 1 ); break; case 'Enter': if (highlightedIndex >= 0 && highlightedIndex < autocompleteResults.length) { e.preventDefault(); handleSelectAutocomplete(autocompleteResults[highlightedIndex]); } break; case 'Escape': setShowAutocomplete(false); setHighlightedIndex(-1); break; } }; // 폼 제출 처리 const handleSubmit = (e: React.FormEvent) => { e.preventDefault(); if (!inputValue.trim()) return; if (searchType === 'name' && selectedItem && onAutocomplete) { // 업체명 검색인 경우 autocomplete API 호출 const request: AutocompleteRequest = { address: selectedItem.address, roadAddress: selectedItem.roadAddress, title: selectedItem.title, }; onAutocomplete(request); } else { // URL 검색인 경우 기존 로직 onAnalyze(inputValue.trim(), searchType); } }; return (
{/* 로고 */}
ADO2
{/* URL 입력 폼 */}
{/* 드롭다운 */}
{isDropdownOpen && (
{searchTypeOptions.map((option) => ( ))}
)}
{/* 입력 필드 */}
{ const value = e.target.value; setInputValue(value); // 업체명 검색일 때 자동완성 검색 (디바운스) if (searchType === 'name') { if (debounceRef.current) { clearTimeout(debounceRef.current); } debounceRef.current = setTimeout(() => { handleAutocompleteSearch(value); }, 300); } }} onFocus={() => { if (searchType === 'name' && autocompleteResults.length > 0) { setShowAutocomplete(true); } }} placeholder={getPlaceholder()} className="url-input-field" /> {/* 자동완성 결과 */} {showAutocomplete && searchType === 'name' && (
{isAutocompleteLoading ? (
{t('urlInput.searching')}
) : ( autocompleteResults.map((item, index) => ( )) )}
)}
{/* 검색 버튼 */}
{/* 에러 메시지 */} {error && (

{error}

)}
{/* 안내 텍스트 */}

{getGuideText()}

{/* 테스트 버튼 (VITE_IS_TESTPAGE=true일 때만 표시) */} {isTestPage && onTestData && ( )}
); }; export default UrlInputContent;