// js/components/ResultPage.jsx
// 결과 페이지 - 탭 없는 순차 스크롤 구조 / 파스텔톤 배경 / 이미지저장+카카오공유
window.ResultPage = function({ result, onRestart }) {
const { useState, useCallback, useEffect } = React;
const helpers = window.PCODE_HELPERS;
const [feedback, setFeedback] = useState(null); // 'good' | 'bad' | null
const [isCapturing, setIsCapturing] = useState(false); // 이미지 저장 로딩
const { typeData, productName, category, price, pcode, scores, axes } = result;
// 스크롤 상단으로
useEffect(() => {
window.scrollTo({ top: 0, behavior: 'smooth' });
}, []);
// Recharts
const { RadarChart, PolarGrid, PolarAngleAxis, PolarRadiusAxis,
Radar, ResponsiveContainer, Tooltip } = Recharts;
// 레이더 데이터
const radarData = [
{ axis: 'U/S', value: scores.us, fullMark: 100 },
{ axis: 'T/C', value: scores.tc, fullMark: 100 },
{ axis: 'F/E', value: scores.fe, fullMark: 100 },
{ axis: 'P/G', value: scores.pg, fullMark: 100 },
];
// 4축 바 데이터
const axisData = [
{ id:'us', code:axes.us.code, percentage:scores.us,
leftLabel:'U (실용)', rightLabel:'S (스타일)',
leftColor:'#3B82F6', rightColor:'#EC4899',
description: helpers.getAxisDescription('US', axes.us.code, scores.us) },
{ id:'tc', code:axes.tc.code, percentage:scores.tc,
leftLabel:'T (트렌드)', rightLabel:'C (클래식)',
leftColor:'#10B981', rightColor:'#F59E0B',
description: helpers.getAxisDescription('TC', axes.tc.code, scores.tc) },
{ id:'fe', code:axes.fe.code, percentage:scores.fe,
leftLabel:'F (기능)', rightLabel:'E (감성)',
leftColor:'#EF4444', rightColor:'#A855F7',
description: helpers.getAxisDescription('FE', axes.fe.code, scores.fe) },
{ id:'pg', code:axes.pg.code, percentage:scores.pg,
leftLabel:'P (개인용)', rightLabel:'G (선물용)',
leftColor:'#9CA3AF', rightColor:'#FBBF24',
description: helpers.getAxisDescription('PG', axes.pg.code, scores.pg) },
];
// 커스텀 레이더 레이블
const CustomAxisTick = ({ x, y, payload }) => {
const C = { 'U/S':'#6B7AF7','T/C':'#10B981','F/E':'#EF4444','P/G':'#F59E0B' };
return (
{payload.value}
);
};
/* ════════════════════════════════════════
📷 이미지 저장 (html2canvas)
════════════════════════════════════════ */
const handleDownloadImage = useCallback(async () => {
setIsCapturing(true);
try {
// html2canvas 동적 로드
if (!window.html2canvas) {
await new Promise((resolve, reject) => {
const s = document.createElement('script');
s.src = 'https://cdn.jsdelivr.net/npm/html2canvas@1.4.1/dist/html2canvas.min.js';
s.onload = resolve;
s.onerror = reject;
document.head.appendChild(s);
});
}
// 캡처 대상: 전체 결과 페이지
const target = document.getElementById('result-capture-area');
if (!target) { alert('캡처 영역을 찾을 수 없습니다.'); return; }
const canvas = await window.html2canvas(target, {
scale: 2,
useCORS: true,
backgroundColor: '#ffffff',
scrollY: -window.scrollY,
windowWidth: document.documentElement.scrollWidth,
windowHeight: document.documentElement.scrollHeight,
});
// PNG 다운로드
const link = document.createElement('a');
const safeProductName = productName.replace(/[^가-힣a-zA-Z0-9]/g, '_');
link.download = `${safeProductName}_${pcode}_PCODE결과.png`;
link.href = canvas.toDataURL('image/png');
link.click();
} catch (err) {
console.error('이미지 저장 실패:', err);
alert('이미지 저장에 실패했습니다.\n브라우저의 인쇄(Ctrl+P) 기능으로 PDF를 저장해보세요.');
} finally {
setIsCapturing(false);
}
}, [productName, pcode]);
/* ════════════════════════════════════════
💬 카카오톡 공유
– Kakao SDK 없이 카카오 링크 공유 페이지를 이용
– 실제 배포 환경에서는 Kakao SDK + 앱 키를 세팅하세요
════════════════════════════════════════ */
const handleKakaoShare = useCallback(() => {
const msg = [
`🎯 [P-CODE 진단 결과]`,
``,
`제품명: ${productName}`,
`카테고리: ${category} | 가격: ${helpers.formatPrice(price)}원`,
``,
`${typeData?.emoji} P-CODE: ${pcode}`,
`유형: The ${typeData?.name} (${typeData?.name_kr})`,
``,
`"${typeData?.description?.substring(0, 80)}..."`,
``,
`💡 핵심 메시지: ${typeData?.marketing_tone?.core}`,
``,
`👉 지금 당신의 제품 P-CODE를 진단해보세요!`,
].join('\n');
// Kakao SDK가 로드된 경우 SDK 공유, 아니면 텍스트 복사로 fallback
if (window.Kakao && window.Kakao.isInitialized && window.Kakao.isInitialized()) {
window.Kakao.Share.sendDefault({
objectType: 'text',
text: msg,
link: { mobileWebUrl: window.location.href, webUrl: window.location.href },
});
} else {
// fallback: 클립보드 복사 후 안내
if (navigator.clipboard) {
navigator.clipboard.writeText(msg).then(() => {
alert('📋 결과 내용이 클립보드에 복사되었습니다!\n카카오톡을 열어 붙여넣기(Ctrl+V) 해주세요.');
}).catch(() => {
// 최후 fallback: kakaolink 웹 공유
const encoded = encodeURIComponent(msg.substring(0, 500));
window.open(`https://sharer.kakao.com/talk/friends/picker/link?app_key=YOUR_APP_KEY&text=${encoded}`, '_blank', 'width=500,height=600');
});
} else {
const encoded = encodeURIComponent(msg.substring(0, 500));
window.open(`https://sharer.kakao.com/talk/friends/picker/link?app_key=YOUR_APP_KEY&text=${encoded}`, '_blank', 'width=500,height=600');
}
}
}, [productName, category, price, pcode, typeData, helpers]);
/* ════════════════════════════════════════
공통 컴포넌트
════════════════════════════════════════ */
const SectionHeader = ({ emoji, title, subtitle }) => (
{emoji} {title}
{subtitle && (
{subtitle}
)}
);
const SubHeader = ({ emoji, title, color }) => (
{emoji} {title}
);
// 카드 공통 스타일
const card = {
background:'#fff', borderRadius:'20px',
border:'1px solid #E5E7EB', padding:'24px',
marginBottom:'16px', boxShadow:'0 1px 4px rgba(0,0,0,0.04)'
};
return (
{/* ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
1️⃣ 최상단 헤더 — 파스텔 보라 배경
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ */}
{/* ── 본문 캡처 대상 영역 시작 ── */}
{/* ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
2️⃣ 레이더 차트
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ */}
} tickLine={false} />
{
if (!payload?.[0]) return null;
return (
{payload[0].payload.axis}
{payload[0].value}%
);
}} />
{axisData.map(item => (
{item.leftLabel}
{item.rightLabel}
{item.description}
))}
{/* 구분선 */}
{/* ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
3️⃣ 유형 분석 — 연한 노란 배경 섹션
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ */}
{/* 유형 카드 */}
{typeData?.emoji}
The {typeData?.name}
({pcode})
{typeData?.name_kr}
{typeData?.description}
{/* 소비자 프로필 */}
{typeData?.consumer_profile}
{/* 코드 해석 */}
{[
{ code:axes.us.code, axis:'U/S', labels:{ U:'실용 중심', S:'스타일 중심' }, color:'#3B82F6' },
{ code:axes.tc.code, axis:'T/C', labels:{ T:'트렌드 지향', C:'클래식 지향' }, color:'#10B981' },
{ code:axes.fe.code, axis:'F/E', labels:{ F:'기능 우선', E:'감성 우선' }, color:'#EF4444' },
{ code:axes.pg.code, axis:'P/G', labels:{ P:'개인용', G:'선물용' }, color:'#F59E0B' },
].map((item, i) => (
{item.code}
{item.axis}축
{item.labels[item.code]}
))}
{/* 핵심 인사이트 */}
{/* 구분선 */}
{/* ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
4️⃣ 전략 가이드 — 연한 초록 배경 섹션
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ */}
{/* 4-1 추천 전략 */}
{typeData?.recommended_strategies?.map((strategy, i) => {
const dotIdx = strategy.indexOf('. ');
const title = dotIdx > 0 ? strategy.substring(0, dotIdx+1) : strategy;
const detail = dotIdx > 0 ? strategy.substring(dotIdx+2) : '';
return (
{i+1}
{title}
{detail && (
{detail}
)}
);
})}
{/* 4-2 마케팅 톤 */}
💡 핵심 메시지
"{typeData?.marketing_tone?.core}"
✍️ 추천 카피 예시
{typeData?.marketing_tone?.examples?.map((ex, i) => (
))}
{/* 4-3 패키징 */}
{typeData?.packaging_concept}
{/* 구분선 */}
{/* ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
5️⃣ 시장 데이터 — 연한 분홍 배경 섹션
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ */}
{/* 5-1 채널 */}
🎯 1순위 채널
{typeData?.channel_priority}
📱 전체 추천 채널
{typeData?.recommended_channels?.map((ch, i) => (
{i === 0 && '⭐ '}{ch}
))}
{/* 5-2 유사 제품 */}
이 유형과 유사한 성공 제품들
{typeData?.product_examples?.map((ex, i) => (
))}
{/* 5-3 페르소나 */}
핵심 소비자
"{typeData?.consumer_persona}"
이들의 특징
{[
typeData?.consumer_profile?.split('. ')[0] + '.',
typeData?.consumer_profile?.split('. ')[1]
? typeData?.consumer_profile?.split('. ')[1] + '.' : null,
`${typeData?.marketing_tone?.core}에 강하게 반응합니다.`,
`주요 채널: ${typeData?.recommended_channels?.slice(0,2).join(', ')}`,
].filter(Boolean).map((feat, i) => (
))}
{/* 5-4 시장 통찰 */}
{[
{ icon:'📊', label:'시장 규모 및 성향', color:'#DC2626', bg:'#FEF2F2', border:'#FECACA',
text: typeData?.insight },
{ icon:'🌐', label:'주요 활동 플랫폼', color:'#2563EB', bg:'#EFF6FF', border:'#BFDBFE',
text: `${typeData?.channel_priority} 채널에서 가장 높은 전환율을 보입니다. ${typeData?.recommended_channels?.slice(0,3).join(', ')} 순서로 마케팅 예산을 배분하세요.` },
{ icon:'💼', label:'기업을 위한 조언', color:'#7C3AED', bg:'#FAF5FF', border:'#E9D5FF',
text: `"${typeData?.marketing_tone?.core}"를 핵심 메시지로 삼아 모든 채널에서 일관되게 전달하세요. 소비자 페르소나인 "${typeData?.consumer_persona}"를 중심으로 타겟 마케팅을 설계하면 전환율 극대화가 가능합니다.` },
].map((item, i) => (
))}
{/* 구분선 */}
{/* ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
6️⃣ 다음 단계 — 연한 파랑 배경 섹션
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ */}
{/* 6-1 심화 AI 진단 */}
P-CODE 기반의 기초 진단을 넘어, AI가 귀사 제품의
상품 경쟁력 · 콘텐츠 완성도 · 판매 구조 를
심층 분석합니다. 더 정밀한 데이터 인사이트와 맞춤 전략 보고서를 제공합니다.
{[
{ check:'상품 경쟁력 분석', detail:'동일 카테고리 경쟁사 대비 가격·기능·포지셔닝 분석, 차별화 포인트 도출' },
{ check:'콘텐츠 완성도 분석', detail:'상세페이지·SNS·광고 소재의 P-CODE 정합성 진단 및 개선 방향 제시' },
{ check:'판매 구조 분석', detail:'유통 채널 구성, 가격 설정, 프로모션 전략의 적합성 종합 평가' },
].map((item, i) => (
✓
{item.check}
{item.detail}
))}
💰 가격: 별도 문의
⏱️ 24시간 내 결과 제공
📊 심화 진단 신청하기
{/* 6-2 마케팅 컨설팅 */}
P-CODE 진단 결과를 토대로, 각 분야 전문가와 1:1로 실질적인 성장 전략을 수립합니다.
이론이 아닌 현장 경험 기반의 맞춤 솔루션 을 제공합니다.
📍 분야별 전문가 연결
{[
{ role:'MD 전문가', desc:'유통 전략, 바이어 미팅, 채널 입점 가이드', icon:'🏬' },
{ role:'마케팅 전문가', desc:'광고 소재, 캠페인 기획, 퍼포먼스 마케팅', icon:'📣' },
{ role:'브랜딩 전문가', desc:'브랜드 아이덴티티, 스토리텔링, 포지셔닝', icon:'🎨' },
{ role:'유통 전문가', desc:'오프라인/온라인 유통망 확장 전략 수립', icon:'🚚' },
].map((item, i) => (
))}
📅 마케팅 컨설팅 예약하기
{/* ─────────────────────────────────────────
6-3 결과 저장 및 공유 ← 수정 핵심 영역
───────────────────────────────────────── */}
{/* 버튼 1: 이미지로 저장하기 */}
{ if (!isCapturing) {
e.currentTarget.style.background = '#C4B5FD';
e.currentTarget.style.transform = 'translateY(-2px)';
e.currentTarget.style.boxShadow = '0 6px 16px rgba(109,40,217,0.18)';
}}}
onMouseLeave={e => { if (!isCapturing) {
e.currentTarget.style.background = '#DDD6FE';
e.currentTarget.style.transform = 'translateY(0)';
e.currentTarget.style.boxShadow = '0 2px 8px rgba(109,40,217,0.08)';
}}}
>
{isCapturing
? <>
저장 중...>
: <>📷 이미지로 저장하기>
}
{/* 버튼 2: 카카오톡으로 보내기 */}
{
e.currentTarget.style.background = '#FDE047';
e.currentTarget.style.transform = 'translateY(-2px)';
e.currentTarget.style.boxShadow = '0 6px 16px rgba(251,191,36,0.3)';
}}
onMouseLeave={e => {
e.currentTarget.style.background = '#FEF08A';
e.currentTarget.style.transform = 'translateY(0)';
e.currentTarget.style.boxShadow = '0 2px 8px rgba(251,191,36,0.15)';
}}
>
💬 카카오톡으로 보내기
💾 이 결과는 브라우저에 자동 저장되었습니다 (최근 10개 유지)
{/* 구분선 */}
{/* ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
7️⃣ 피드백 & 하단 액션
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ */}
{/* 피드백 */}
이 결과가 도움이 되었나요?
솔직한 피드백이 더 나은 진단을 만듭니다
{[
{ key:'good', label:'👍 정확함',
active:{ background:'#F0FDF4', borderColor:'#22C55E', color:'#15803D' },
inactive:{ background:'#fff', borderColor:'#E5E7EB', color:'#6B7280' } },
{ key:'bad', label:'👎 보완 필요',
active:{ background:'#FEF2F2', borderColor:'#EF4444', color:'#B91C1C' },
inactive:{ background:'#fff', borderColor:'#E5E7EB', color:'#6B7280' } },
].map(btn => (
setFeedback(btn.key)}
style={{
padding:'12px 24px', borderRadius:'12px', border:'2px solid',
fontWeight:700, fontSize:'0.9rem', cursor:'pointer',
transition:'all 0.2s',
...(feedback === btn.key ? btn.active : btn.inactive)
}}>
{btn.label}
))}
{feedback && (
{feedback === 'good'
? '🙏 피드백 감사합니다! 계속 발전하겠습니다.'
: '📝 소중한 의견 감사합니다. 더 정확하게 개선하겠습니다!'}
)}
{/* 다시 진단 */}
🔄 다시 진단하기
📊 심화 AI 진단 신청
📈 마케팅 컨설팅 예약
진단 완료: {helpers.formatDate(result.timestamp)}
{/* ── 캡처 영역 끝 ── */}
{/* 반응형 + 스피너 인라인 스타일 */}
);
};