206 lines
7.1 KiB
TypeScript
206 lines
7.1 KiB
TypeScript
|
|
import React, { useState, useRef, useCallback } from 'react';
|
|
import { searchNaverLocal, NaverLocalSearchItem, AutocompleteRequest } from '../../utils/api';
|
|
|
|
type SearchType = 'url' | 'name';
|
|
|
|
interface UrlInputContentProps {
|
|
onAnalyze: (value: string, type?: SearchType) => void;
|
|
onAutocomplete?: (data: AutocompleteRequest) => void;
|
|
error: string | null;
|
|
}
|
|
|
|
const UrlInputContent: React.FC<UrlInputContentProps> = ({ onAnalyze, onAutocomplete, error }) => {
|
|
const [inputValue, setInputValue] = useState('');
|
|
const [searchType, setSearchType] = useState<SearchType>('url');
|
|
const [isDropdownOpen, setIsDropdownOpen] = useState(false);
|
|
const [autocompleteResults, setAutocompleteResults] = useState<NaverLocalSearchItem[]>([]);
|
|
const [isAutocompleteLoading, setIsAutocompleteLoading] = useState(false);
|
|
const [showAutocomplete, setShowAutocomplete] = useState(false);
|
|
const debounceRef = useRef<NodeJS.Timeout | null>(null);
|
|
const autocompleteRef = useRef<HTMLDivElement>(null);
|
|
|
|
const handleSubmit = (e: React.FormEvent) => {
|
|
e.preventDefault();
|
|
if (inputValue.trim()) {
|
|
onAnalyze(inputValue.trim(), searchType);
|
|
}
|
|
};
|
|
|
|
const searchTypeOptions = [
|
|
{ value: 'url' as SearchType, label: 'URL' },
|
|
{ value: 'name' as SearchType, label: '업체명' },
|
|
];
|
|
|
|
const getPlaceholder = () => {
|
|
return searchType === 'url'
|
|
? 'https://www.castad.com'
|
|
: '업체명을 입력하세요';
|
|
};
|
|
|
|
const getGuideText = () => {
|
|
return searchType === 'url'
|
|
? 'URL에서 가져온 정보로 영상이 자동 생성됩니다.'
|
|
: '업체명으로 검색하여 정보를 가져옵니다.';
|
|
};
|
|
|
|
// 업체명 검색 시 자동완성 (디바운스 적용)
|
|
const handleAutocompleteSearch = useCallback(async (query: string) => {
|
|
if (!query.trim() || searchType !== 'name') {
|
|
setAutocompleteResults([]);
|
|
setShowAutocomplete(false);
|
|
return;
|
|
}
|
|
|
|
setIsAutocompleteLoading(true);
|
|
try {
|
|
const response = await searchNaverLocal(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: NaverLocalSearchItem) => {
|
|
const request: AutocompleteRequest = {
|
|
address: item.address,
|
|
roadAddress: item.roadAddress,
|
|
title: item.title,
|
|
};
|
|
|
|
setInputValue(item.title.replace(/<[^>]*>/g, '')); // HTML 태그 제거
|
|
setShowAutocomplete(false);
|
|
setAutocompleteResults([]);
|
|
|
|
if (onAutocomplete) {
|
|
onAutocomplete(request);
|
|
}
|
|
};
|
|
|
|
return (
|
|
<div className="url-input-container">
|
|
<div className="url-input-content">
|
|
{/* 로고 */}
|
|
<div className="url-input-logo">
|
|
<img src="/assets/images/ado2-logo.svg" alt="ADO2" />
|
|
</div>
|
|
|
|
{/* URL 입력 폼 */}
|
|
<form onSubmit={handleSubmit} className="url-input-form">
|
|
<div className="url-input-wrapper">
|
|
{/* 드롭다운 */}
|
|
<div className="url-input-dropdown-container">
|
|
<button
|
|
type="button"
|
|
className="url-input-dropdown-trigger"
|
|
onClick={() => setIsDropdownOpen(!isDropdownOpen)}
|
|
>
|
|
<span>{searchTypeOptions.find(opt => opt.value === searchType)?.label}</span>
|
|
<svg
|
|
width="12"
|
|
height="12"
|
|
viewBox="0 0 24 24"
|
|
fill="none"
|
|
stroke="currentColor"
|
|
strokeWidth="2"
|
|
className={`url-input-dropdown-arrow ${isDropdownOpen ? 'open' : ''}`}
|
|
>
|
|
<path d="M6 9l6 6 6-6" />
|
|
</svg>
|
|
</button>
|
|
{isDropdownOpen && (
|
|
<div className="url-input-dropdown-menu">
|
|
{searchTypeOptions.map((option) => (
|
|
<button
|
|
key={option.value}
|
|
type="button"
|
|
className={`url-input-dropdown-item ${searchType === option.value ? 'active' : ''}`}
|
|
onClick={() => {
|
|
setSearchType(option.value);
|
|
setIsDropdownOpen(false);
|
|
}}
|
|
>
|
|
{option.label}
|
|
</button>
|
|
))}
|
|
</div>
|
|
)}
|
|
</div>
|
|
|
|
{/* 입력 필드 */}
|
|
<div className="url-input-field-container" ref={autocompleteRef}>
|
|
<input
|
|
type={searchType === 'url' ? 'url' : 'text'}
|
|
value={inputValue}
|
|
onChange={(e) => {
|
|
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' && (
|
|
<div className="url-input-autocomplete-dropdown">
|
|
{isAutocompleteLoading ? (
|
|
<div className="url-input-autocomplete-loading">검색 중...</div>
|
|
) : (
|
|
autocompleteResults.map((item, index) => (
|
|
<button
|
|
key={index}
|
|
type="button"
|
|
className="url-input-autocomplete-item"
|
|
onMouseDown={(e) => {
|
|
e.preventDefault();
|
|
handleSelectAutocomplete(item);
|
|
}}
|
|
>
|
|
<div className="url-input-autocomplete-title" dangerouslySetInnerHTML={{ __html: item.title }} />
|
|
<div className="url-input-autocomplete-address">{item.roadAddress || item.address}</div>
|
|
</button>
|
|
))
|
|
)}
|
|
</div>
|
|
)}
|
|
</div>
|
|
</div>
|
|
|
|
{/* 에러 메시지 */}
|
|
{error && (
|
|
<p className="url-input-error">{error}</p>
|
|
)}
|
|
</form>
|
|
|
|
{/* 안내 텍스트 */}
|
|
<p className="url-input-guide">
|
|
{getGuideText()}
|
|
</p>
|
|
</div>
|
|
</div>
|
|
);
|
|
};
|
|
|
|
export default UrlInputContent;
|