From ad625e08eeec934d01ca46cfcad5523303a1148a Mon Sep 17 00:00:00 2001 From: Haewon Kam Date: Fri, 3 Apr 2026 16:05:33 +0900 Subject: [PATCH] fix: enrichment pipeline reliability + loading page gradient + button click area MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - generate-report: filter empty strings from AI social handles, add saveError logging - useReport: 3-level fallback for social handles (report > clinicInfo > scrape_data) - useEnrichment: always trigger enrichment if clinicName exists (not just IG/YT handles) - Hero: pointer-events-none on decorative blobs (were blocking button clicks) - AnalysisLoadingPage: warm gradient on INFINITH logo text (#fff3eb → #e4cfff → #f5f9ff) Co-Authored-By: Claude Opus 4.6 (1M context) --- src/components/Hero.tsx | 6 ++-- src/hooks/useEnrichment.ts | 3 +- src/hooks/useReport.ts | 31 +++++++++++++-------- src/pages/AnalysisLoadingPage.tsx | 7 ++++- supabase/functions/generate-report/index.ts | 17 ++++++++--- 5 files changed, 43 insertions(+), 21 deletions(-) diff --git a/src/components/Hero.tsx b/src/components/Hero.tsx index b205bbd..452fcd0 100644 --- a/src/components/Hero.tsx +++ b/src/components/Hero.tsx @@ -79,9 +79,9 @@ export default function Hero() { {/* Decorative elements */} -
-
-
+
+
+
); } diff --git a/src/hooks/useEnrichment.ts b/src/hooks/useEnrichment.ts index 25e4f77..b98cb0c 100644 --- a/src/hooks/useEnrichment.ts +++ b/src/hooks/useEnrichment.ts @@ -34,8 +34,7 @@ export function useEnrichment( useEffect(() => { if (!baseReport || !params?.reportId || hasTriggered.current) return; - // Don't enrich if no social handles are available - if (!params.instagramHandle && !params.instagramHandles?.length && !params.youtubeChannelId) return; + // Always enrich if clinicName exists — Naver, 강남언니, Google Maps work with name alone hasTriggered.current = true; setStatus('loading'); diff --git a/src/hooks/useReport.ts b/src/hooks/useReport.ts index 3412e4d..c57195d 100644 --- a/src/hooks/useReport.ts +++ b/src/hooks/useReport.ts @@ -76,21 +76,30 @@ export function useReport(id: string | undefined): UseReportResult { }, ); - // Recover social handles: report.socialHandles > scrape_data.clinic.socialMedia - let handles = (reportJson.socialHandles as Record) || null; - if (!handles && scrapeData) { - const clinic = scrapeData.clinic as Record | undefined; - const socialMedia = clinic?.socialMedia as Record | undefined; - if (socialMedia) { + // Recover social handles: report.socialHandles > AI clinicInfo.socialMedia > scrape_data + let handles = (reportJson.socialHandles as Record) || null; + if (!handles) { + // Try AI-generated socialMedia in clinicInfo + const aiSocial = (reportJson.clinicInfo as Record)?.socialMedia as Record | undefined; + const scrapeSocial = scrapeData + ? (scrapeData.clinic as Record)?.socialMedia as Record | undefined + : undefined; + + const igSource = aiSocial?.instagramAccounts || aiSocial?.instagram || scrapeSocial?.instagram; + const ytSource = (aiSocial?.youtube as string) || scrapeSocial?.youtube; + + if (igSource || ytSource) { handles = { - instagram: normalizeInstagramHandle(socialMedia.instagram), - youtube: socialMedia.youtube || null, - facebook: socialMedia.facebook || null, - blog: socialMedia.blog || null, + instagram: Array.isArray(igSource) + ? igSource.map((h: string) => normalizeInstagramHandle(h)).filter(Boolean) + : normalizeInstagramHandle(igSource as string), + youtube: ytSource || null, + facebook: (aiSocial?.facebook as string) || scrapeSocial?.facebook || null, + blog: (aiSocial?.naverBlog as string) || scrapeSocial?.blog || null, }; } } - setSocialHandles(handles); + setSocialHandles(handles as Record | null); // If channelEnrichment already exists in DB, merge it immediately const enrichment = reportJson.channelEnrichment as EnrichmentData | undefined; diff --git a/src/pages/AnalysisLoadingPage.tsx b/src/pages/AnalysisLoadingPage.tsx index 806677a..a969469 100644 --- a/src/pages/AnalysisLoadingPage.tsx +++ b/src/pages/AnalysisLoadingPage.tsx @@ -81,7 +81,12 @@ export default function AnalysisLoadingPage() { initial={{ opacity: 0, y: 20 }} animate={{ opacity: 1, y: 0 }} transition={{ duration: 0.6 }} - className="text-4xl md:text-5xl font-serif font-bold text-gradient mb-4" + className="text-4xl md:text-5xl font-serif font-bold mb-4" + style={{ + background: 'linear-gradient(to right, #fff3eb, #e4cfff, #f5f9ff)', + WebkitBackgroundClip: 'text', + WebkitTextFillColor: 'transparent', + }} > INFINITH diff --git a/supabase/functions/generate-report/index.ts b/supabase/functions/generate-report/index.ts index c422ddd..0a6206a 100644 --- a/supabase/functions/generate-report/index.ts +++ b/supabase/functions/generate-report/index.ts @@ -187,11 +187,15 @@ ${JSON.stringify(analyzeResult.data?.analysis || {}, null, 2)} .filter((h): h is string => h !== null) )]; + // Filter out empty strings — AI sometimes returns "" instead of null + const pickNonEmpty = (...vals: (string | null | undefined)[]): string | null => + vals.find(v => v && v.trim().length > 0) || null; + const normalizedHandles = { - instagram: igHandles.length > 0 ? igHandles : null, // array of handles - youtube: aiSocial.youtube || scrapeSocial.youtube || null, - facebook: aiSocial.facebook || scrapeSocial.facebook || null, - blog: aiSocial.naverBlog || scrapeSocial.blog || null, + instagram: igHandles.length > 0 ? igHandles : null, + youtube: pickNonEmpty(aiSocial.youtube, scrapeSocial.youtube), + facebook: pickNonEmpty(aiSocial.facebook, scrapeSocial.facebook), + blog: pickNonEmpty(aiSocial.naverBlog, scrapeSocial.blog), }; // Embed normalized handles in report for DB persistence @@ -211,6 +215,10 @@ ${JSON.stringify(analyzeResult.data?.analysis || {}, null, 2)} .select("id") .single(); + if (saveError) { + console.error("DB save error:", saveError); + } + return new Response( JSON.stringify({ success: true, @@ -226,6 +234,7 @@ ${JSON.stringify(analyzeResult.data?.analysis || {}, null, 2)} aiGeneration: !report.parseError, }, socialHandles: normalizedHandles, + saveError: saveError?.message || null, address, services, },