500 lines
24 KiB
JavaScript
500 lines
24 KiB
JavaScript
import React from 'react';
|
|
import { useNavigate } from 'react-router-dom';
|
|
import DesktopHero from '../_shell/DesktopHero';
|
|
import DesktopFooter from '../_shell/DesktopFooter';
|
|
import PanelHeader from '../_shell/PanelHeader';
|
|
import OrnamentBloom from '../_shell/OrnamentBloom';
|
|
import { IconChevron, IconPaw } from '../_shell/Icons';
|
|
import deriveTraits from '../_shell/helpers/deriveTraits';
|
|
import daeunLabel from '../_shell/helpers/daeunLabel';
|
|
import hexA from '../_shell/helpers/hexA';
|
|
|
|
const HANJA_TO_ID = { '木': 'wood', '火': 'fire', '土': 'earth', '金': 'metal', '水': 'water' };
|
|
const ID_TO_KO = { wood: '목', fire: '화', earth: '토', metal: '금', water: '수' };
|
|
const ID_TO_CH = { wood: '木', fire: '火', earth: '土', metal: '金', water: '水' };
|
|
const ID_TO_COLOR = {
|
|
wood: '#4E6B5C', fire: '#C04A4A', earth: '#A67B3F',
|
|
metal: '#D4AF37', water: '#3A5A8C',
|
|
};
|
|
const STEM_EL = { '甲': 'wood', '乙': 'wood', '丙': 'fire', '丁': 'fire', '戊': 'earth', '己': 'earth', '庚': 'metal', '辛': 'metal', '壬': 'water', '癸': 'water' };
|
|
const BRANCH_EL = { '子': 'water', '丑': 'earth', '寅': 'wood', '卯': 'wood', '辰': 'earth', '巳': 'fire', '午': 'fire', '未': 'earth', '申': 'metal', '酉': 'metal', '戌': 'earth', '亥': 'water' };
|
|
|
|
const PILLAR_LABELS = { year: '년주', month: '월주', day: '일주', hour: '시주' };
|
|
|
|
function elementsByEngId(scores = {}) {
|
|
const out = {};
|
|
for (const [key, value] of Object.entries(scores || {})) {
|
|
const id = HANJA_TO_ID[key] || key;
|
|
if (ID_TO_KO[id]) out[id] = Number(value) || 0;
|
|
}
|
|
return out;
|
|
}
|
|
|
|
function maxElement(elementsObj) {
|
|
return ['wood', 'fire', 'earth', 'metal', 'water']
|
|
.map((id) => ({ id, value: Math.round(elementsObj[id] || 0), color: ID_TO_COLOR[id] }))
|
|
.reduce((best, item) => (item.value > best.value ? item : best), { id: 'metal', value: 0, color: '#D4AF37' });
|
|
}
|
|
|
|
function normalizePillar(pillar = {}, key) {
|
|
const stem = pillar.stem || '-';
|
|
const branch = pillar.branch || '-';
|
|
return {
|
|
id: key,
|
|
label: PILLAR_LABELS[key],
|
|
cheongan: {
|
|
ch: stem,
|
|
ko: pillar.stem_kr || '',
|
|
mark: stem && STEM_EL[stem] ? `(${ID_TO_KO[STEM_EL[stem]]})` : '',
|
|
color: ID_TO_COLOR[STEM_EL[stem]] || '#1F2A44',
|
|
},
|
|
jiji: {
|
|
ch: branch,
|
|
ko: pillar.branch_kr || '',
|
|
mark: branch && BRANCH_EL[branch] ? `(${ID_TO_KO[BRANCH_EL[branch]]})` : '',
|
|
color: ID_TO_COLOR[BRANCH_EL[branch]] || '#1F2A44',
|
|
},
|
|
sipsin: pillar.ten_god || '-',
|
|
jijang: pillar.hidden_stems || pillar.fortune || '-',
|
|
};
|
|
}
|
|
|
|
function readingToDesktopData(reading) {
|
|
const saju = reading?.saju_data || {};
|
|
const elementsObj = elementsByEngId(reading?.analysis_data?.element_scores);
|
|
const strongest = maxElement(elementsObj);
|
|
const pillars = ['year', 'month', 'day', 'hour'].map((key) => normalizePillar(saju[key], key));
|
|
const daeun = (reading?.daeun_data || []).map((item) => ({
|
|
age: `${item.age}~${item.age + 9}세`,
|
|
rawAge: item.age,
|
|
gan: item.stem || item.gan || '-',
|
|
label: daeunLabel(item.age),
|
|
current: item.start_year <= new Date().getFullYear() && new Date().getFullYear() <= item.end_year,
|
|
startYear: item.start_year,
|
|
endYear: item.end_year,
|
|
}));
|
|
const fallbackDaeun = [0, 10, 20, 30, 40, 50, 60, 70].map((age, index) => ({
|
|
age: `${age}~${age + 9}세`,
|
|
rawAge: age,
|
|
gan: ['戊', '丁', '丙', '乙', '甲', '癸', '壬', '辛'][index],
|
|
label: daeunLabel(age),
|
|
current: age === 30,
|
|
}));
|
|
|
|
return {
|
|
name: reading?.name || '백호',
|
|
gender: reading?.gender === 'female' ? '여' : '남',
|
|
birth: `${reading?.birth_year || '1990'}년 ${reading?.birth_month || '01'}월 ${reading?.birth_day || '01'}일 ${reading?.birth_hour ?? '10'}:00`,
|
|
lunar: reading?.calendar_type === 'lunar' ? '음력 입력' : '양력 입력',
|
|
birthPlace: reading?.birth_place || '서울특별시',
|
|
ilgan: pillars[2]?.cheongan || { ch: '庚', color: '#3A5A8C' },
|
|
pillars,
|
|
elementsObj,
|
|
ohaeng: ['wood', 'fire', 'earth', 'metal', 'water'].map((id) => ({
|
|
id, ko: ID_TO_KO[id], ch: ID_TO_CH[id],
|
|
value: Math.round(elementsObj[id] || ({ wood: 20, fire: 35, earth: 25, metal: 55, water: 30 }[id])),
|
|
color: ID_TO_COLOR[id],
|
|
})),
|
|
strongest,
|
|
summary: reading?.interpretation_json?.summary || '의리가 강하고 책임감이 뛰어난 흐름입니다. 목표를 정하면 끝까지 해내는 추진력과 원칙을 중시하는 태도가 장점으로 드러납니다.',
|
|
traits: deriveTraits(elementsObj, []),
|
|
daeun: daeun.length ? daeun : fallbackDaeun,
|
|
dayMasterStrength: reading?.analysis_data?.day_master_strength,
|
|
};
|
|
}
|
|
|
|
export default function SajuDesktop({ reading }) {
|
|
const navigate = useNavigate();
|
|
const data = readingToDesktopData(reading);
|
|
|
|
return (
|
|
<main className="page paper-bg screen-in" style={{ marginTop: -78, paddingTop: 78 }}>
|
|
<DesktopHero
|
|
title="사주풀이"
|
|
subtitle="당신의 사주 구조와 흐름을 깊이 있게 풀어드립니다."
|
|
accent="#D4AF37"
|
|
bubble={<div>사주의 흐름을 읽고,<br />당신의 길을 밝혀드립니다.</div>}
|
|
/>
|
|
|
|
<div style={{ maxWidth: 1400, margin: '0 auto', padding: '0 36px 32px' }}>
|
|
<BasicInfoBar data={data} onEdit={() => navigate('/saju')} />
|
|
|
|
<div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr 360px', gap: 18, marginTop: 20 }}>
|
|
<SajuStructureCard data={data} />
|
|
<OhaengCard data={data} />
|
|
<HoryungInsightCard data={data} />
|
|
</div>
|
|
|
|
<div style={{ display: 'grid', gridTemplateColumns: 'repeat(5, 1fr)', gap: 12, marginTop: 18 }}>
|
|
<TraitDeskCard color="#A67B3F" iconName="will" title="핵심 성향" body={data.summary} />
|
|
<TraitDeskCard color="#4E6B5C" iconName="adapt" title="강점" bullets={data.traits.slice(0, 4).map((t) => t.ko || t.label)} />
|
|
<TraitDeskCard color="#C04A4A" iconName="challenge" title="주의할 점" bullets={['고집이 강할 수 있음', '완벽주의 경향', '휴식이 부족해지기 쉬움']} />
|
|
<TraitDeskCard color="#3A5A8C" iconName="lead" title="직업운" body={`${ID_TO_KO[data.strongest.id]}(${ID_TO_CH[data.strongest.id]}) 기운을 중심으로 체계적이고 집중력이 필요한 분야에서 강점이 드러납니다.`} />
|
|
<TraitDeskCard color="#D89098" iconName="heart" title="연애운" body="신뢰와 안정감을 중시하며 깊이 있는 관계를 만들어갑니다. 따뜻한 표현이 관계의 열쇠입니다." />
|
|
</div>
|
|
|
|
<DaeunDeskCard data={data} />
|
|
<ConsultCTA onClick={() => navigate('/saju/me')} />
|
|
</div>
|
|
|
|
<DesktopFooter />
|
|
</main>
|
|
);
|
|
}
|
|
|
|
function BasicInfoBar({ data, onEdit }) {
|
|
return (
|
|
<div className="k-frame" style={{ padding: '18px 24px', display: 'flex', alignItems: 'center', gap: 32 }}>
|
|
<div style={{ display: 'flex', alignItems: 'center', gap: 10 }}>
|
|
<div style={{
|
|
width: 40, height: 40, borderRadius: 10,
|
|
background: 'rgba(212,175,55,0.10)', border: '1px solid rgba(212,175,55,0.4)',
|
|
display: 'flex', alignItems: 'center', justifyContent: 'center', color: '#B89530',
|
|
}}>
|
|
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="#B89530" strokeWidth="1.6" strokeLinecap="round" strokeLinejoin="round">
|
|
<rect x="5" y="4" width="14" height="17" rx="2" />
|
|
<path d="M8 8h8M8 12h8M8 16h5" />
|
|
</svg>
|
|
</div>
|
|
<div className="font-title" style={{ fontSize: 18, color: '#1F2A44', letterSpacing: '-0.02em' }}>기본 정보</div>
|
|
</div>
|
|
<InfoCol label="이름" value={data.name} />
|
|
<InfoCol label="성별" value={data.gender} />
|
|
<InfoCol label="양력" value={data.birth} />
|
|
<InfoCol label="음력" value={data.lunar} />
|
|
<InfoCol label="출생지" value={data.birthPlace} />
|
|
<InfoCol label="사주명리" value={<span>양 · 일간 <span className="font-title" style={{ color: data.ilgan.color, fontSize: 14 }}>{data.ilgan.ch}</span></span>} />
|
|
<div style={{ flex: 1 }} />
|
|
<button onClick={onEdit} style={{
|
|
padding: '8px 18px', borderRadius: 999,
|
|
background: 'transparent', border: '1px solid rgba(31,42,68,0.2)',
|
|
color: '#6B6B6B', fontSize: 12, fontWeight: 700, whiteSpace: 'nowrap',
|
|
}}>정보 수정</button>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
function InfoCol({ label, value }) {
|
|
return (
|
|
<div style={{ display: 'flex', flexDirection: 'column', gap: 4, minWidth: 0 }}>
|
|
<div style={{ fontSize: 10, color: '#9A968D', letterSpacing: '-0.01em', fontWeight: 700 }}>{label}</div>
|
|
<div style={{ fontSize: 13, color: '#1F2A44', whiteSpace: 'nowrap', letterSpacing: '-0.01em' }}>{value}</div>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
function SajuStructureCard({ data }) {
|
|
return (
|
|
<div className="k-frame" style={{ padding: '20px 22px' }}>
|
|
<PanelHeader title="사주 구조" />
|
|
<table style={{ width: '100%', borderCollapse: 'separate', borderSpacing: 0 }}>
|
|
<thead>
|
|
<tr>
|
|
<th style={thStyle()} />
|
|
{data.pillars.map((pillar) => (
|
|
<th key={pillar.id} style={thStyle({ active: pillar.id === 'day' })}>
|
|
{pillar.id === 'day' && (
|
|
<div style={{
|
|
fontSize: 9, fontWeight: 700, color: '#F7F2E8', background: '#6A4C7C',
|
|
padding: '2px 8px', borderRadius: 99, display: 'inline-block', marginBottom: 4,
|
|
}}>일간</div>
|
|
)}
|
|
<div style={{ fontSize: 12, color: '#1F2A44', fontWeight: 700 }}>{pillar.label}</div>
|
|
</th>
|
|
))}
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
<Row label="천간" cells={data.pillars.map((pillar) => pillar.cheongan)} day />
|
|
<Row label="지지" cells={data.pillars.map((pillar) => pillar.jiji)} day />
|
|
<RowText label="십신" cells={data.pillars.map((pillar) => pillar.sipsin)} />
|
|
<RowText label="지장간" cells={data.pillars.map((pillar) => pillar.jijang)} mono />
|
|
</tbody>
|
|
</table>
|
|
<div style={{
|
|
marginTop: 12, padding: '10px 14px',
|
|
background: 'rgba(106,76,124,0.06)', borderRadius: 8,
|
|
border: '1px dashed rgba(106,76,124,0.25)',
|
|
fontSize: 11.5, color: '#6B6B6B', lineHeight: 1.6,
|
|
}}>
|
|
※ 일간(나)을 중심으로 사주의 흐름과 균형을 해석합니다.
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
const thStyle = ({ active = false } = {}) => ({
|
|
padding: '8px 4px 12px',
|
|
textAlign: 'center',
|
|
borderBottom: '1px solid rgba(31,42,68,0.08)',
|
|
background: active ? 'rgba(106,76,124,0.06)' : 'transparent',
|
|
position: 'relative',
|
|
});
|
|
|
|
function Row({ label, cells, day }) {
|
|
return (
|
|
<tr>
|
|
<td style={{ fontSize: 11, color: '#9A968D', fontWeight: 700, padding: '14px 8px', textAlign: 'center' }}>{label}</td>
|
|
{cells.map((cell, index) => {
|
|
const isDay = index === 2 && day;
|
|
return (
|
|
<td key={`${cell.ch}-${index}`} style={{
|
|
padding: '10px 4px', textAlign: 'center',
|
|
background: isDay ? 'rgba(106,76,124,0.06)' : 'transparent',
|
|
borderTop: '1px solid rgba(31,42,68,0.04)',
|
|
}}>
|
|
<div className="font-title" style={{ fontSize: 28, color: cell.color, lineHeight: 1, letterSpacing: 0 }}>{cell.ch}</div>
|
|
<div style={{ fontSize: 9.5, color: hexA(cell.color, 0.9), fontWeight: 700, marginTop: 3, letterSpacing: '-0.02em' }}>
|
|
{cell.ko} <span style={{ color: '#9A968D', fontWeight: 500 }}>{cell.mark}</span>
|
|
</div>
|
|
</td>
|
|
);
|
|
})}
|
|
</tr>
|
|
);
|
|
}
|
|
|
|
function RowText({ label, cells, mono }) {
|
|
return (
|
|
<tr>
|
|
<td style={{ fontSize: 11, color: '#9A968D', fontWeight: 700, padding: '10px 8px', textAlign: 'center' }}>{label}</td>
|
|
{cells.map((cell, index) => (
|
|
<td key={`${cell}-${index}`} style={{
|
|
padding: '8px 4px', textAlign: 'center',
|
|
background: index === 2 ? 'rgba(106,76,124,0.06)' : 'transparent',
|
|
borderTop: '1px solid rgba(31,42,68,0.04)',
|
|
fontFamily: mono ? 'var(--font-title)' : 'inherit',
|
|
fontSize: mono ? 13 : 12,
|
|
color: '#1F2A44',
|
|
letterSpacing: mono ? '0.1em' : '-0.01em',
|
|
}}>{String(cell || '-')}</td>
|
|
))}
|
|
</tr>
|
|
);
|
|
}
|
|
|
|
function OhaengCard({ data }) {
|
|
const strongest = maxElement(data.elementsObj);
|
|
return (
|
|
<div className="k-frame" style={{ padding: '20px 22px' }}>
|
|
<PanelHeader title="오행 분석" />
|
|
<div style={{ display: 'flex', alignItems: 'flex-end', justifyContent: 'space-around', height: 160, gap: 8, padding: '0 8px' }}>
|
|
{data.ohaeng.map((element) => {
|
|
const height = Math.min(100, Math.max(8, (element.value / 60) * 100));
|
|
return (
|
|
<div key={element.id} style={{ flex: 1, display: 'flex', flexDirection: 'column', alignItems: 'center', height: '100%' }}>
|
|
<div style={{ flex: 1, width: '100%', display: 'flex', flexDirection: 'column', justifyContent: 'flex-end' }}>
|
|
<div style={{ fontSize: 11, color: element.color, fontWeight: 700, textAlign: 'center', marginBottom: 4 }}>{element.value}%</div>
|
|
<div style={{
|
|
width: 28, margin: '0 auto', height: `${height}%`, minHeight: 6,
|
|
background: `linear-gradient(180deg, ${hexA(element.color, 0.85)}, ${element.color})`,
|
|
borderRadius: '6px 6px 2px 2px',
|
|
boxShadow: `0 -2px 8px ${hexA(element.color, 0.3)}, inset 0 1px 0 rgba(255,255,255,0.3)`,
|
|
}} />
|
|
</div>
|
|
<div style={{ marginTop: 8, fontSize: 12, color: '#1F2A44', fontWeight: 700, display: 'flex', alignItems: 'baseline', gap: 3 }}>
|
|
{element.ko}<span style={{ fontSize: 10, color: '#9A968D' }}>({element.ch})</span>
|
|
</div>
|
|
</div>
|
|
);
|
|
})}
|
|
</div>
|
|
<div style={{
|
|
marginTop: 16, padding: '12px 14px',
|
|
background: hexA(strongest.color, 0.08), borderRadius: 8,
|
|
border: `1px solid ${hexA(strongest.color, 0.2)}`,
|
|
}}>
|
|
<div style={{ fontSize: 13, fontWeight: 700, color: strongest.color, marginBottom: 4 }}>
|
|
{ID_TO_KO[strongest.id]}({ID_TO_CH[strongest.id]})의 기운이 강한 사주입니다.
|
|
</div>
|
|
<div style={{ fontSize: 12, color: '#6B6B6B', lineHeight: 1.6 }}>
|
|
강한 기운을 바탕으로 장점을 살리고 부족한 기운은 생활 습관과 관계에서 보완해 보세요.
|
|
</div>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
function HoryungInsightCard({ data }) {
|
|
const strongest = maxElement(data.elementsObj);
|
|
const items = [
|
|
{ title: `일간이 ${data.ilgan.ch}이시네요.`, desc: '단단한 중심과 자기 기준을 갖고 흐름을 읽는 힘이 있습니다.' },
|
|
{ title: `${ID_TO_KO[strongest.id]}(${ID_TO_CH[strongest.id]})의 기운이 두드러져요.`, desc: '해당 기운의 장점을 생활과 일의 방향으로 살려보세요.' },
|
|
{ title: '균형을 보완하면 더욱 좋아요.', desc: '강한 기운만 밀어붙이기보다 부족한 기운을 의식하면 흐름이 부드러워집니다.' },
|
|
{ title: '지금의 선택이 미래의 나를 만듭니다.', desc: '작은 실천을 꾸준히 쌓는 시기로 삼아보세요.' },
|
|
];
|
|
return (
|
|
<div className="k-frame dark" style={{ padding: '22px 22px', display: 'flex', flexDirection: 'column', gap: 14 }}>
|
|
<div style={{ display: 'flex', alignItems: 'center', justifyContent: 'center', gap: 10, color: '#E8C76B' }}>
|
|
<svg width="32" height="6" viewBox="0 0 32 6"><path d="M0 3 L28 3" stroke="#E8C76B" strokeWidth="1" /><circle cx="30" cy="3" r="1.5" fill="#E8C76B" /></svg>
|
|
<h3 className="font-title" style={{ margin: 0, fontSize: 17, color: '#E8C76B', letterSpacing: '-0.01em' }}>호령이의 해설</h3>
|
|
<svg width="32" height="6" viewBox="0 0 32 6"><circle cx="2" cy="3" r="1.5" fill="#E8C76B" /><path d="M4 3 L32 3" stroke="#E8C76B" strokeWidth="1" /></svg>
|
|
</div>
|
|
<div style={{ display: 'flex', flexDirection: 'column', gap: 14, marginTop: 4 }}>
|
|
{items.map((item, index) => (
|
|
<div key={item.title} style={{ display: 'flex', gap: 10, alignItems: 'flex-start' }}>
|
|
<div style={{
|
|
width: 32, height: 32, borderRadius: '50%',
|
|
background: 'rgba(212,175,55,0.12)', border: '1px solid rgba(212,175,55,0.35)',
|
|
display: 'flex', alignItems: 'center', justifyContent: 'center', flexShrink: 0,
|
|
color: '#E8C76B', fontSize: 13, fontWeight: 800,
|
|
}}>{index + 1}</div>
|
|
<div>
|
|
<div style={{ fontSize: 13, fontWeight: 700, color: '#F7F2E8', marginBottom: 3 }}>{item.title}</div>
|
|
<div style={{ fontSize: 11.5, color: '#D9D2C0', lineHeight: 1.55 }}>{item.desc}</div>
|
|
</div>
|
|
</div>
|
|
))}
|
|
</div>
|
|
<div style={{
|
|
marginTop: 6, padding: '14px 16px', borderRadius: 10,
|
|
background: 'rgba(212,175,55,0.08)', border: '1px solid rgba(212,175,55,0.3)',
|
|
textAlign: 'center',
|
|
}}>
|
|
<div className="font-title" style={{ fontSize: 14, color: '#E8C76B', lineHeight: 1.5 }}>
|
|
지금의 선택이<br />미래의 나를 만듭니다.
|
|
<span style={{ marginLeft: 4, opacity: 0.7 }}><IconPaw size={11} color="#E8C76B" /></span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
function TraitDeskCard({ color, iconName, title, body, bullets }) {
|
|
return (
|
|
<div className="k-frame" style={{ padding: '18px 18px' }}>
|
|
<div style={{ display: 'flex', alignItems: 'center', gap: 8, marginBottom: 10 }}>
|
|
<div style={{
|
|
width: 32, height: 32, borderRadius: '50%',
|
|
background: hexA(color, 0.10), border: `1px solid ${hexA(color, 0.35)}`,
|
|
display: 'flex', alignItems: 'center', justifyContent: 'center', flexShrink: 0,
|
|
}}>
|
|
<TraitIcon name={iconName} color={color} size={16} />
|
|
</div>
|
|
<div className="font-title" style={{ fontSize: 15, color: '#1F2A44', letterSpacing: '-0.02em' }}>{title}</div>
|
|
</div>
|
|
{body && <div style={{ fontSize: 12, color: '#6B6B6B', lineHeight: 1.65 }}>{body}</div>}
|
|
{bullets && (
|
|
<ul style={{ margin: 0, padding: 0, listStyle: 'none', display: 'flex', flexDirection: 'column', gap: 6 }}>
|
|
{bullets.map((item) => (
|
|
<li key={item} style={{ fontSize: 12, color: '#1F2A44', display: 'flex', gap: 6 }}>
|
|
<span style={{ color, flexShrink: 0 }}>·</span> {item}
|
|
</li>
|
|
))}
|
|
</ul>
|
|
)}
|
|
</div>
|
|
);
|
|
}
|
|
|
|
function TraitIcon({ name, color, size }) {
|
|
const common = { width: size, height: size, viewBox: '0 0 24 24', fill: 'none', stroke: color, strokeWidth: '1.7', strokeLinecap: 'round', strokeLinejoin: 'round' };
|
|
if (name === 'heart') return <svg {...common}><path d="M12 20s-7-4.5-7-10a4 4 0 0 1 7-2.6A4 4 0 0 1 19 10c0 5.5-7 10-7 10z" /></svg>;
|
|
if (name === 'challenge') return <svg {...common}><path d="M12 3l10 17H2z" /><path d="M12 10v5M12 18v.5" /></svg>;
|
|
if (name === 'lead') return <svg {...common}><path d="M4 16c4-6 8-6 16 0" /><path d="M8 12l4-4 4 4" /></svg>;
|
|
if (name === 'adapt') return <svg {...common}><path d="M4 12c2-4 5-6 8-6s6 2 8 6c-2 4-5 6-8 6s-6-2-8-6z" /><circle cx="12" cy="12" r="2" /></svg>;
|
|
return <svg {...common}><path d="M12 3v18M5 9l7-6 7 6M6 17h12" /></svg>;
|
|
}
|
|
|
|
function DaeunDeskCard({ data }) {
|
|
const current = data.daeun.find((item) => item.current) || data.daeun[0];
|
|
return (
|
|
<div className="k-frame" style={{ padding: '22px 24px', marginTop: 18 }}>
|
|
<div style={{ display: 'flex', alignItems: 'center', gap: 12, marginBottom: 14 }}>
|
|
<OrnamentBloom size={20} color="#D4AF37" />
|
|
<h3 className="font-title" style={{ margin: 0, fontSize: 18, color: '#1F2A44', letterSpacing: '-0.02em' }}>대운 흐름</h3>
|
|
<span style={{ fontSize: 12, color: '#9A968D' }}>10년 단위 운의 흐름을 살펴보세요.</span>
|
|
</div>
|
|
<div style={{ display: 'grid', gridTemplateColumns: '2fr 1.2fr', gap: 20, alignItems: 'flex-start' }}>
|
|
<div style={{ display: 'flex', alignItems: 'center', gap: 4 }}>
|
|
{data.daeun.map((item, index) => (
|
|
<React.Fragment key={`${item.age}-${index}`}>
|
|
<DaeunNodeDesk {...item} />
|
|
{index < data.daeun.length - 1 && (
|
|
<div style={{ flex: 1, display: 'flex', alignItems: 'center', justifyContent: 'center', minWidth: 12 }}>
|
|
<IconChevron dir="right" size={12} color={item.current || data.daeun[index + 1].current ? '#6A4C7C' : '#D4AF37'} />
|
|
</div>
|
|
)}
|
|
</React.Fragment>
|
|
))}
|
|
</div>
|
|
<div style={{
|
|
background: 'rgba(212,175,55,0.06)', borderRadius: 10,
|
|
border: '1px dashed rgba(212,175,55,0.4)', padding: '14px 16px',
|
|
}}>
|
|
<div style={{ display: 'flex', alignItems: 'center', gap: 6, marginBottom: 8 }}>
|
|
<span style={{
|
|
fontSize: 10, fontWeight: 700, color: '#F7F2E8', background: '#6A4C7C',
|
|
padding: '2px 8px', borderRadius: 99,
|
|
}}>현재</span>
|
|
<span style={{ fontSize: 13, fontWeight: 700, color: '#1F2A44' }}>대운 해설 ({current?.age})</span>
|
|
</div>
|
|
<div style={{ fontSize: 12, color: '#6B6B6B', lineHeight: 1.7 }}>
|
|
자기 확장과 기반을 다지는 시기입니다.<br />
|
|
꾸준한 노력과 인내가 결실을 맺고, 커리어와 재정적 성장이 기대됩니다.
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
function DaeunNodeDesk({ age, gan, label, current }) {
|
|
return (
|
|
<div style={{ display: 'flex', flexDirection: 'column', alignItems: 'center', gap: 4, position: 'relative', minWidth: 64 }}>
|
|
{current && (
|
|
<div style={{
|
|
position: 'absolute', top: -8, left: '50%', transform: 'translateX(-50%)',
|
|
fontSize: 9, fontWeight: 700, color: '#F7F2E8', background: '#6A4C7C',
|
|
padding: '2px 8px', borderRadius: 99, zIndex: 1, whiteSpace: 'nowrap',
|
|
}}>현재</div>
|
|
)}
|
|
<div style={{ fontSize: 10, color: '#9A968D', marginTop: current ? 12 : 4, fontWeight: 700, whiteSpace: 'nowrap' }}>{age}</div>
|
|
<div style={{
|
|
width: 48, height: 58, borderRadius: '50% 50% 40% 40%',
|
|
background: current ? '#1F2A44' : '#FBF7EF',
|
|
border: current ? '2px solid #D4AF37' : '1px solid rgba(31,42,68,0.12)',
|
|
display: 'flex', alignItems: 'center', justifyContent: 'center',
|
|
boxShadow: current ? '0 4px 14px rgba(31,42,68,0.3)' : 'none',
|
|
}}>
|
|
<span className="font-title" style={{ fontSize: 22, color: current ? '#E8C76B' : '#1F2A44' }}>{gan}</span>
|
|
</div>
|
|
<div style={{ fontSize: 11, color: current ? '#6A4C7C' : '#6B6B6B', fontWeight: current ? 700 : 500 }}>{label}</div>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
function ConsultCTA({ onClick }) {
|
|
return (
|
|
<div style={{
|
|
marginTop: 18, padding: '28px 32px',
|
|
background: '#1F2A44', color: '#F7F2E8',
|
|
borderRadius: 14, border: '1px solid rgba(212,175,55,0.4)',
|
|
display: 'grid', gridTemplateColumns: '1fr auto', alignItems: 'center', gap: 24,
|
|
boxShadow: '0 12px 40px rgba(31,42,68,0.18)',
|
|
}}>
|
|
<div>
|
|
<div style={{ display: 'flex', alignItems: 'center', gap: 10, marginBottom: 6, color: '#E8C76B' }}>
|
|
<OrnamentBloom size={16} color="#E8C76B" />
|
|
<span style={{ fontSize: 12, fontWeight: 700, letterSpacing: '0.1em' }}>1:1 PERSONAL CONSULT</span>
|
|
</div>
|
|
<div className="font-title" style={{ fontSize: 22, color: '#F7F2E8', letterSpacing: '-0.02em' }}>
|
|
더 깊은 해석이 필요하신가요?
|
|
</div>
|
|
<div style={{ marginTop: 6, fontSize: 13, color: '#D9D2C0' }}>
|
|
개인 맞춤 상담을 통해 당신의 사주를 더 깊이 이해하고 명확한 방향을 찾아보세요.
|
|
</div>
|
|
</div>
|
|
<button onClick={onClick} style={{
|
|
padding: '14px 24px', borderRadius: 99,
|
|
background: '#E8C76B', color: '#1F2A44',
|
|
border: 'none', fontSize: 14, fontWeight: 800,
|
|
boxShadow: '0 6px 18px rgba(232,199,107,0.4), inset 0 1px 0 rgba(255,255,255,0.4)',
|
|
display: 'flex', alignItems: 'center', gap: 8, whiteSpace: 'nowrap',
|
|
}}>
|
|
1:1 상담 신청하기 <IconPaw size={14} color="#1F2A44" />
|
|
</button>
|
|
</div>
|
|
);
|
|
}
|