벌크 자막/추출 개선, 검색 필터 무시, geocoding 필드 수정, 네이버맵 링크
- 벌크 자막: 브라우저 우선 + API fallback, 광고 즉시 skip, 대기 시간 단축 - 벌크 자막/추출: 선택한 영상만 처리 가능 (체크박스 선택 후 실행) - 자막 실패 시 no_transcript 상태 마킹하여 재시도 방지 - 검색 시 필터 조건 무시 (채널/장르/가격/지역/영역 초기화) - 리셋 버튼 클릭 시 검색어 입력란 초기화 - RestaurantMapper updateFields에 google_place_id, rating 등 geocoding 필드 추가 - SearchMapper에 tabling_url, catchtable_url, phone, website 필드 추가 - 식당 상세에 네이버 지도 링크 추가 - YouTubeService.getTranscriptApi public 전환 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -154,6 +154,8 @@ export default function Home() {
|
||||
const [myReviews, setMyReviews] = useState<(Review & { restaurant_id: string; restaurant_name: string | null })[]>([]);
|
||||
const [visits, setVisits] = useState<{ today: number; total: number } | null>(null);
|
||||
const [userLoc, setUserLoc] = useState<{ lat: number; lng: number }>({ lat: 37.498, lng: 127.0276 });
|
||||
const [isSearchResult, setIsSearchResult] = useState(false);
|
||||
const [resetCount, setResetCount] = useState(0);
|
||||
const geoApplied = useRef(false);
|
||||
|
||||
const regionTree = useMemo(() => buildRegionTree(restaurants), [restaurants]);
|
||||
@@ -174,6 +176,13 @@ export default function Home() {
|
||||
const filteredRestaurants = useMemo(() => {
|
||||
const dist = (r: Restaurant) =>
|
||||
(r.latitude - userLoc.lat) ** 2 + (r.longitude - userLoc.lng) ** 2;
|
||||
if (isSearchResult) {
|
||||
return [...restaurants].sort((a, b) => {
|
||||
const da = dist(a), db = dist(b);
|
||||
if (da !== db) return da - db;
|
||||
return (b.rating || 0) - (a.rating || 0);
|
||||
});
|
||||
}
|
||||
return restaurants.filter((r) => {
|
||||
if (channelFilter && !(r.channels || []).includes(channelFilter)) return false;
|
||||
if (cuisineFilter && !matchCuisineFilter(r.cuisine_type, cuisineFilter)) return false;
|
||||
@@ -194,7 +203,7 @@ export default function Home() {
|
||||
if (da !== db) return da - db;
|
||||
return (b.rating || 0) - (a.rating || 0);
|
||||
});
|
||||
}, [restaurants, channelFilter, cuisineFilter, priceFilter, countryFilter, cityFilter, districtFilter, boundsFilterOn, mapBounds, userLoc]);
|
||||
}, [restaurants, isSearchResult, channelFilter, cuisineFilter, priceFilter, countryFilter, cityFilter, districtFilter, boundsFilterOn, mapBounds, userLoc]);
|
||||
|
||||
// Set desktop default to map mode on mount + get user location
|
||||
useEffect(() => {
|
||||
@@ -217,6 +226,7 @@ export default function Home() {
|
||||
// Load restaurants on mount and when channel filter changes
|
||||
useEffect(() => {
|
||||
setLoading(true);
|
||||
setIsSearchResult(false);
|
||||
api
|
||||
.getRestaurants({ limit: 500, channel: channelFilter || undefined })
|
||||
.then(setRestaurants)
|
||||
@@ -253,6 +263,18 @@ export default function Home() {
|
||||
setRestaurants(results);
|
||||
setSelected(null);
|
||||
setShowDetail(false);
|
||||
setIsSearchResult(true);
|
||||
// 검색 시 필터 초기화
|
||||
setChannelFilter("");
|
||||
setCuisineFilter("");
|
||||
setPriceFilter("");
|
||||
setCountryFilter("");
|
||||
setCityFilter("");
|
||||
setDistrictFilter("");
|
||||
setBoundsFilterOn(false);
|
||||
// 검색 결과에 맞게 지도 이동
|
||||
const flyTo = computeFlyTo(results);
|
||||
if (flyTo) setRegionFlyTo(flyTo);
|
||||
} catch (e) {
|
||||
console.error("Search failed:", e);
|
||||
} finally {
|
||||
@@ -350,6 +372,8 @@ export default function Home() {
|
||||
setBoundsFilterOn(false);
|
||||
setShowFavorites(false);
|
||||
setShowMyReviews(false);
|
||||
setIsSearchResult(false);
|
||||
setResetCount((c) => c + 1);
|
||||
api
|
||||
.getRestaurants({ limit: 500 })
|
||||
.then((data) => {
|
||||
@@ -531,7 +555,7 @@ export default function Home() {
|
||||
{/* Row 1: Search + dropdown filters */}
|
||||
<div className="flex items-center gap-3">
|
||||
<div className="w-96 shrink-0">
|
||||
<SearchBar onSearch={handleSearch} isLoading={loading} />
|
||||
<SearchBar key={resetCount} onSearch={handleSearch} isLoading={loading} />
|
||||
</div>
|
||||
<button
|
||||
onClick={handleReset}
|
||||
@@ -717,7 +741,7 @@ export default function Home() {
|
||||
{/* ── Header row 2 (mobile only): search + toolbar ── */}
|
||||
<div className={`md:hidden px-4 pb-3 space-y-2 ${mobileTab === "favorites" || mobileTab === "profile" ? "hidden" : ""}`}>
|
||||
{/* Row 1: Search */}
|
||||
<SearchBar onSearch={handleSearch} isLoading={loading} />
|
||||
<SearchBar key={resetCount} onSearch={handleSearch} isLoading={loading} />
|
||||
{/* Row 2: Toolbar */}
|
||||
<div className="flex items-center gap-2">
|
||||
<button
|
||||
|
||||
Reference in New Issue
Block a user