diff --git a/src/hooks/useReport.ts b/src/hooks/useReport.ts index 2c00029..853fc05 100644 --- a/src/hooks/useReport.ts +++ b/src/hooks/useReport.ts @@ -72,6 +72,16 @@ export function useReport(id: string | undefined): UseReportResult { fetchReportById(id) .then((row) => { const reportJson = row.report as Record; + + // V2 pipeline: if report is empty but status is not 'complete', show message + if (!reportJson || Object.keys(reportJson).length === 0 || reportJson.parseError) { + const status = (row as Record).status as string | undefined; + if (status && status !== 'complete') { + throw new Error(`리포트 생성 중입니다 (${status}). 잠시 후 새로고침 해주세요.`); + } + throw new Error('리포트 데이터가 비어있습니다. 분석을 다시 실행해주세요.'); + } + const scrapeData = row.scrape_data as Record | undefined; const transformed = transformApiReport( diff --git a/src/pages/AnalysisLoadingPage.tsx b/src/pages/AnalysisLoadingPage.tsx index cd91458..b4a2cd7 100644 --- a/src/pages/AnalysisLoadingPage.tsx +++ b/src/pages/AnalysisLoadingPage.tsx @@ -1,53 +1,21 @@ import { useState, useEffect, useRef } from 'react'; import { useNavigate, useLocation } from 'react-router'; -import { motion, AnimatePresence } from 'motion/react'; -import { Check, AlertCircle, CheckCircle2, XCircle } from 'lucide-react'; +import { motion } from 'motion/react'; +import { Check, AlertCircle } from 'lucide-react'; import { discoverChannels, collectChannelData, generateReportV2 } from '../lib/supabase'; -/** - * Pipeline V2: 3-phase analysis with real progress. - * Phase 1: discover-channels → verify social handles - * Phase 2: collect-channel-data → gather all channel data + market analysis - * Phase 3: generate-report → AI report from real data - */ - -interface VerifiedChannel { - handle: string; - verified: boolean; - url?: string; -} - -interface VerifiedChannels { - instagram?: VerifiedChannel[]; - youtube?: VerifiedChannel | null; - facebook?: VerifiedChannel | null; - naverBlog?: VerifiedChannel | null; - gangnamUnni?: VerifiedChannel | null; - tiktok?: VerifiedChannel | null; -} - type Phase = 'discovering' | 'collecting' | 'generating' | 'complete'; const PHASE_STEPS = [ - { key: 'discovering' as Phase, label: '웹사이트 스캔 & 채널 발견 중...', labelDone: '채널 발견 완료' }, - { key: 'collecting' as Phase, label: '채널 데이터 수집 & 시장 분석 중...', labelDone: '데이터 수집 완료' }, - { key: 'generating' as Phase, label: 'AI 마케팅 리포트 생성 중...', labelDone: '리포트 생성 완료' }, - { key: 'complete' as Phase, label: '분석 완료!', labelDone: '분석 완료!' }, + { key: 'discovering' as Phase, label: 'Scanning website & discovering channels...', labelDone: 'Channels discovered' }, + { key: 'collecting' as Phase, label: 'Collecting channel data & market analysis...', labelDone: 'Data collected' }, + { key: 'generating' as Phase, label: 'Generating AI marketing report...', labelDone: 'Report generated' }, + { key: 'complete' as Phase, label: 'Finalizing report...', labelDone: 'Complete' }, ]; -const CHANNEL_LABELS: Record = { - instagram: 'Instagram', - youtube: 'YouTube', - facebook: 'Facebook', - naverBlog: '네이버 블로그', - gangnamUnni: '강남언니', - tiktok: 'TikTok', -}; - export default function AnalysisLoadingPage() { const [phase, setPhase] = useState('discovering'); const [error, setError] = useState(null); - const [verifiedChannels, setVerifiedChannels] = useState(null); const navigate = useNavigate(); const location = useLocation(); const url = (location.state as { url?: string })?.url; @@ -66,40 +34,33 @@ export default function AnalysisLoadingPage() { const runPipeline = async () => { try { - // ─── Phase 1: Discover Channels ─── + // Phase 1: Discover Channels setPhase('discovering'); - const discovery = await discoverChannels(url); if (!discovery.success) throw new Error(discovery.error || 'Channel discovery failed'); - - setVerifiedChannels(discovery.verifiedChannels); const reportId = discovery.reportId; - // ─── Phase 2: Collect Channel Data ─── + // Phase 2: Collect Channel Data setPhase('collecting'); - const collection = await collectChannelData(reportId); if (!collection.success) throw new Error(collection.error || 'Data collection failed'); - // ─── Phase 3: Generate Report ─── + // Phase 3: Generate Report setPhase('generating'); - const result = await generateReportV2(reportId); if (!result.success) throw new Error(result.error || 'Report generation failed'); - // ─── Complete ─── + // Complete — navigate to report setPhase('complete'); setTimeout(() => { navigate(`/report/${reportId}`, { replace: true, - state: { - report: result.report, - metadata: result.metadata, - reportId, - }, + state: result.report && result.metadata + ? { report: result.report, metadata: result.metadata, reportId } + : undefined, }); - }, 1000); + }, 800); } catch (err) { setError(err instanceof Error ? err.message : 'An error occurred'); } @@ -133,7 +94,7 @@ export default function AnalysisLoadingPage() { initial={{ opacity: 0, y: 20 }} animate={{ opacity: 1, y: 0 }} transition={{ duration: 0.6, delay: 0.1 }} - className="text-purple-300/80 text-sm font-mono mb-10 truncate max-w-full" + className="text-purple-300/80 text-sm font-mono mb-12 truncate max-w-full" > {url} @@ -151,28 +112,26 @@ export default function AnalysisLoadingPage() { onClick={() => navigate('/', { replace: true })} className="px-6 py-2 text-sm font-medium text-white bg-white/10 rounded-lg hover:bg-white/20 transition-colors" > - 다시 시도 + Try Again ) : ( <> - {/* Pipeline steps */} -
+
{PHASE_STEPS.map((step, index) => { - const isCompleted = phaseIndex > index; + const isCompleted = phaseIndex > index || (step.key === 'complete' && phase === 'complete'); const isActive = phaseIndex === index && phase !== 'complete'; - const isDone = step.key === 'complete' && phase === 'complete'; return (
- {isCompleted || isDone ? ( + {isCompleted ? ( )}
- + {isCompleted ? step.labelDone : step.label}
@@ -195,46 +158,6 @@ export default function AnalysisLoadingPage() { })}
- {/* Verified channels panel — shows after Phase 1 */} - - {verifiedChannels && ( - -

- 발견된 채널 -

-
- {Object.entries(verifiedChannels).map(([key, value]) => { - if (!value) return null; - const channels = Array.isArray(value) ? value : [value]; - return channels.map((ch, i) => ( -
- {ch.verified ? ( - - ) : ( - - )} - - {CHANNEL_LABELS[key] || key} - {ch.handle && key !== 'gangnamUnni' ? ` @${ch.handle}` : ''} - - - {ch.verified ? 'verified' : 'not found'} - -
- )); - })} -
-
- )} -
- - {/* Progress bar */}