o2o-castad-frontend/src/pages/Dashboard/UrlInputContent.tsx

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;