o2o-castad-frontend/src/pages/Analysis/AnalysisResultSection.tsx

199 lines
6.5 KiB
TypeScript
Executable File

import React from 'react';
import { CrawlingResponse } from '../../types/api';
interface AnalysisResultSectionProps {
onBack: () => void;
onGenerate?: () => void;
data: CrawlingResponse;
}
// 텍스트를 포맷팅 (개행 처리, 제목 스타일링, 해시태그 스타일링)
const formatReportText = (text: string): React.ReactNode[] => {
if (!text) return [];
// 먼저 \n으로 단락 분리
const paragraphs = text.split('\n');
return paragraphs.map((paragraph, pIdx) => {
// 빈 줄은 줄바꿈으로 처리
if (paragraph.trim() === '') {
return <br key={pIdx} />;
}
// 제목 패턴 (한글로 된 짧은 텍스트로 시작하는 경우)
const titleMatch = paragraph.match(/^(타겟 고객|핵심 차별점|지역 특성|시즌별 포인트)$/);
if (titleMatch) {
return (
<div key={pIdx} style={{ marginTop: pIdx > 0 ? '16px' : '0' }}>
<strong style={{ color: '#94FBE0', fontSize: '18px' }}>{paragraph}</strong>
</div>
);
}
// 해시태그 처리
const hashtagParts = paragraph.split(/(#[^\s#]+)/g);
return (
<div key={pIdx} style={{ marginBottom: '4px' }}>
{hashtagParts.map((segment, segIdx) => {
if (segment.startsWith('#')) {
return (
<span key={segIdx} style={{ color: '#A78BFA' }}>
{segment}
</span>
);
}
return segment;
})}
</div>
);
});
};
// 마크다운 report를 섹션별로 파싱
const parseReport = (report: string) => {
if (!report || report.trim() === '') {
return [];
}
const sections: { title: string; content: string }[] = [];
const lines = report.split('\n');
let currentTitle = '';
let currentContent: string[] = [];
let hasMarkdownHeaders = report.includes('## ');
// 마크다운 헤더가 없는 경우 전체 텍스트를 하나의 섹션으로 반환
if (!hasMarkdownHeaders) {
return [{ title: '분석 결과', content: report.trim() }];
}
lines.forEach((line) => {
if (line.startsWith('## ')) {
if (currentTitle || currentContent.length > 0) {
sections.push({ title: currentTitle, content: currentContent.join('\n').trim() });
}
currentTitle = line.replace('## ', '').trim();
currentContent = [];
} else if (!line.startsWith('# ')) {
currentContent.push(line);
}
});
if (currentTitle || currentContent.length > 0) {
sections.push({ title: currentTitle, content: currentContent.join('\n').trim() });
}
return sections.filter(s => s.content && !s.title.includes('JSON'));
};
const AnalysisResultSection: React.FC<AnalysisResultSectionProps> = ({ onBack, onGenerate, data }) => {
const { processed_info, marketing_analysis } = data;
const tags = marketing_analysis.tags || [];
const facilities = marketing_analysis.facilities || [];
const reportSections = parseReport(marketing_analysis.report);
return (
<div className="analysis-container">
{/* Back Button */}
<div className="back-button-container" style={{ marginLeft: 0 }}>
<button onClick={onBack} className="btn-back">
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2">
<path d="M15 18l-6-6 6-6" />
</svg>
</button>
</div>
{/* Header */}
<div className="analysis-header">
<div className="analysis-icon">
<svg className="w-8 h-8" viewBox="0 0 24 24" fill="currentColor">
<path d="M12 2l2.4 7.2L22 12l-7.6 2.4L12 22l-2.4-7.2L2 12l7.6-2.4z" />
</svg>
</div>
<h1 className="page-title"> </h1>
<p className="page-subtitle">
, .
</p>
</div>
{/* Main Content Grid */}
<div className="analysis-grid">
{/* Brand Identity */}
<div className="brand-identity-card">
<div className="brand-header">
<span className="section-title"> </span>
<span className="brand-subtitle">AI </span>
</div>
<div className="brand-info">
<h2 className="brand-name">{processed_info.customer_name}</h2>
<p className="brand-location">{processed_info.region} · {processed_info.detail_region_info}</p>
</div>
{/* Marketing Analysis Summary */}
<div className="report-section">
<div className="report-content custom-scrollbar">
{reportSections.length === 0 ? (
<div>
{marketing_analysis.report
? formatReportText(marketing_analysis.report)
: '분석 결과가 없습니다.'}
</div>
) : (
<div className="report-sections">
{reportSections.map((section, idx) => (
<div key={idx}>
{section.title && <h4 className="report-section-title">{section.title}</h4>}
<div>
{formatReportText(section.content)}
</div>
</div>
))}
</div>
)}
</div>
</div>
</div>
{/* Right Cards */}
<div className="analysis-cards-column">
{/* Main Selling Points (Facilities) */}
<div className="feature-card">
<span className="section-title-purple"> </span>
<div className="tags-wrapper">
{facilities.map((facility, idx) => (
<span key={idx} className="feature-tag">
{facility}
</span>
))}
</div>
</div>
{/* Recommended Target Keywords (Tags) */}
<div className="feature-card">
<span className="section-title"> </span>
<div className="tags-wrapper">
{tags.map((tag, idx) => (
<span key={idx} className="feature-tag">
{tag}
</span>
))}
</div>
</div>
</div>
</div>
{/* Bottom Button */}
<div className="analysis-bottom">
<button onClick={onGenerate} className="btn-primary">
</button>
</div>
</div>
);
};
export default AnalysisResultSection;