import React, { useState, useEffect, useRef } from 'react'; import { CrawlingResponse, TargetPersona, SellingPoint } from '../../types/api'; interface AnalysisResultSectionProps { onBack: () => void; onGenerate?: () => void; data: CrawlingResponse; } const SparklesIcon = ({ className = '' }: { className?: string }) => ( ); const MapPinIcon = () => ( ); // 범용 애니메이션 아이템 컴포넌트 interface AnimatedItemProps { children: React.ReactNode; index: number; baseDelay?: number; className?: string; } const AnimatedItem: React.FC = ({ children, index, baseDelay = 0, className = '' }) => { const [isVisible, setIsVisible] = useState(false); const delay = baseDelay + index * 150; useEffect(() => { const timer = setTimeout(() => { setIsVisible(true); }, delay); return () => clearTimeout(timer); }, [delay]); return (
{children}
); }; // 애니메이션 USP 아이템 컴포넌트 interface AnimatedUSPItemProps { usp: SellingPoint; index: number; isTop?: boolean; } const AnimatedUSPItem: React.FC = ({ usp, index, isTop = false }) => { const [isVisible, setIsVisible] = useState(false); const delay = index * 150; useEffect(() => { const timer = setTimeout(() => { setIsVisible(true); }, delay); return () => clearTimeout(timer); }, [delay]); if (isTop) { return (
{usp.english_category} TOP
{usp.korean_category}
{usp.description}
); } return (
{usp.english_category}
{usp.korean_category}
{usp.description}
); }; // 레이더 차트 컴포넌트 interface RadarChartProps { data: SellingPoint[]; size?: number; } const RadarChart: React.FC = ({ data, size = 280 }) => { const [animatedScores, setAnimatedScores] = useState(data.map(() => 0)); const [isAnimating, setIsAnimating] = useState(true); useEffect(() => { const targetScores = data.map(item => item.score); const duration = 1500; const startTime = Date.now(); const animate = () => { const elapsed = Date.now() - startTime; const progress = Math.min(elapsed / duration, 1); const easeProgress = 1 - Math.pow(1 - progress, 3); const newScores = targetScores.map(target => target * easeProgress); setAnimatedScores(newScores); if (progress < 1) { requestAnimationFrame(animate); } else { setIsAnimating(false); } }; requestAnimationFrame(animate); }, [data]); const padding = 60; const center = size / 2 + padding; const maxRadius = size / 2 - 20; const levels = 5; const angleStep = (2 * Math.PI) / data.length; const getPoint = (index: number, score: number) => { const angle = angleStep * index - Math.PI / 2; const radius = (score / 100) * maxRadius; return { x: center + radius * Math.cos(angle), y: center + radius * Math.sin(angle), }; }; const getLabelPosition = (index: number) => { const angle = angleStep * index - Math.PI / 2; const radius = maxRadius + 25; return { x: center + radius * Math.cos(angle), y: center + radius * Math.sin(angle), }; }; const dataPoints = animatedScores.map((score, i) => getPoint(i, score)); const dataPath = dataPoints.map((p, i) => (i === 0 ? `M ${p.x} ${p.y}` : `L ${p.x} ${p.y}`)).join(' ') + ' Z'; const levelPaths = Array.from({ length: levels }, (_, levelIndex) => { const levelRadius = ((levelIndex + 1) / levels) * maxRadius; const points = data.map((_, i) => { const angle = angleStep * i - Math.PI / 2; return { x: center + levelRadius * Math.cos(angle), y: center + levelRadius * Math.sin(angle), }; }); return points.map((p, i) => (i === 0 ? `M ${p.x} ${p.y}` : `L ${p.x} ${p.y}`)).join(' ') + ' Z'; }); const axisLines = data.map((_, i) => { const angle = angleStep * i - Math.PI / 2; const endX = center + maxRadius * Math.cos(angle); const endY = center + maxRadius * Math.sin(angle); return `M ${center} ${center} L ${endX} ${endY}`; }); const svgSize = size + padding * 2; return (
{levelPaths.map((path, i) => ( ))} {axisLines.map((path, i) => ( ))} {dataPoints.map((point, i) => ( ))} {data.map((item, i) => { const pos = getLabelPosition(i); const isLeft = pos.x < center - 10; const isRight = pos.x > center + 10; const isTop = pos.y < center - 10; const isBottom = pos.y > center + 10; let textAnchor: 'start' | 'middle' | 'end' = 'middle'; let dy = 0; if (isLeft) textAnchor = 'end'; else if (isRight) textAnchor = 'start'; if (isTop && !isLeft && !isRight) dy = -8; else if (isBottom && !isLeft && !isRight) dy = 5; return ( {item.korean_category} {item.english_category} ); })}
); }; const AnalysisResultSection: React.FC = ({ onBack, onGenerate, data }) => { const { processed_info, marketing_analysis } = data; const containerRef = useRef(null); const [buttonPosition, setButtonPosition] = useState({ left: 0, width: 0 }); const brandIdentity = marketing_analysis?.brand_identity; const marketPositioning = marketing_analysis?.market_positioning; const targetPersonas = marketing_analysis?.target_persona || []; const sellingPoints = marketing_analysis?.selling_points || []; const targetKeywords = marketing_analysis?.target_keywords || []; useEffect(() => { const updateButtonPosition = () => { if (containerRef.current) { const rect = containerRef.current.getBoundingClientRect(); setButtonPosition({ left: rect.left, width: rect.width }); } }; updateButtonPosition(); window.addEventListener('resize', updateButtonPosition); const observer = new MutationObserver(updateButtonPosition); observer.observe(document.body, { attributes: true, subtree: true, attributeFilter: ['class'] }); return () => { window.removeEventListener('resize', updateButtonPosition); observer.disconnect(); }; }, []); const topUSP = sellingPoints.length > 0 ? [...sellingPoints].sort((a, b) => b.score - a.score)[0] : null; return (
{/* Header */}
{/* Page Header */}
Star

브랜드 인텔리전스

AI 데이터 분석을 통해 도출된 {processed_info?.customer_name || '브랜드'}의 핵심 전략입니다.

{/* Main Grid */}
{/* 왼쪽 컬럼 */}
{/* 브랜드 정체성 카드 */}
브랜드 정체성

{processed_info?.customer_name || '브랜드명'}

{processed_info?.detail_region_info || '주소 정보 없음'}

{processed_info?.region || ''}

입지 특성 분석

{brandIdentity?.location_feature_analysis || '정보 없음'}

컨셉 확장성

{brandIdentity?.concept_scalability || '정보 없음'}

{/* 시장 포지셔닝 카드 */}

시장 포지셔닝

핵심 가치 (Core Value)
{marketPositioning?.core_value || '정보 없음'}
카테고리 정의
{marketPositioning?.category_definition || '정보 없음'}
{/* 타겟 페르소나 카드 */}

타겟 페르소나

{targetPersonas.length === 0 &&

정보 없음

} {targetPersonas.map((persona: TargetPersona, idx: number) => (
{persona.persona}
{persona.age.min_age}~{persona.age.max_age}세
{persona.favor_target.length > 0 && (
{persona.favor_target.map((favor: string, i: number) => ( {favor} ))}
)} {persona.decision_trigger && (

Trigger: {persona.decision_trigger}

)}
))}
{/* 오른쪽 컬럼 */}
{/* 주요 셀링 포인트 카드 */}

주요 셀링 포인트 (USP)

{/* 레이더 차트 */} {sellingPoints.length > 0 && (
)} {/* Top USP 하이라이트 */} {topUSP && ( )} {/* 나머지 USP 리스트 */}
{sellingPoints.length === 0 &&

정보 없음

} {sellingPoints .filter((usp: SellingPoint) => usp.english_category !== topUSP?.english_category) .map((usp: SellingPoint, idx: number) => ( ))}
{/* 추천 타겟 키워드 */}

추천 타겟 키워드

{targetKeywords.length === 0 && 정보 없음} {targetKeywords.map((keyword: string, idx: number) => ( # {keyword} ))}
{/* 콘텐츠 생성 버튼 */}
); }; export default AnalysisResultSection;