diff --git a/src/pages/saju/views/saju.mobile.jsx b/src/pages/saju/views/saju.mobile.jsx new file mode 100644 index 0000000..7c0dd0c --- /dev/null +++ b/src/pages/saju/views/saju.mobile.jsx @@ -0,0 +1,424 @@ +import React, { useState } from 'react'; +import { useNavigate } from 'react-router-dom'; +import TopRibbon from '../_shell/TopRibbon'; +import TitleBlock from '../_shell/TitleBlock'; +import Mascot from '../_shell/Mascot'; +import MascotBubble from '../_shell/MascotBubble'; +import OrnateFrame from '../_shell/OrnateFrame'; +import OrnamentBloom from '../_shell/OrnamentBloom'; +import PrimaryButton from '../_shell/PrimaryButton'; +import { IconChevron, IconSparkle } from '../_shell/Icons'; +import deriveTraits from '../_shell/helpers/deriveTraits'; +import daeunLabel from '../_shell/helpers/daeunLabel'; +import hexA from '../_shell/helpers/hexA'; + +// 한자 element key → english id (deriveTraits 입력 표준화) +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', +}; + +function elementsByEngId(scores) { + if (!scores) return {}; + const out = {}; + for (const [hanja, val] of Object.entries(scores)) { + const id = HANJA_TO_ID[hanja]; + if (id) out[id] = val; + } + return out; +} + +function pillarStemColor(saju, pillarKey) { + const stem = saju?.[pillarKey]?.stem; + // 천간 → 오행 매핑 (간략 — 핵심 색만) + const STEM_EL = { '甲':'wood','乙':'wood','丙':'fire','丁':'fire','戊':'earth','己':'earth','庚':'metal','辛':'metal','壬':'water','癸':'water' }; + return ID_TO_COLOR[STEM_EL[stem]] || '#1F2A44'; +} + +function pillarBranchColor(saju, pillarKey) { + const branch = saju?.[pillarKey]?.branch; + const BRANCH_EL = { '子':'water','丑':'earth','寅':'wood','卯':'wood','辰':'earth','巳':'fire','午':'fire','未':'earth','申':'metal','酉':'metal','戌':'earth','亥':'water' }; + return ID_TO_COLOR[BRANCH_EL[branch]] || '#1F2A44'; +} + +const TABS = [ + ['basic', '기본정보'], + ['chart', '사주명식'], + ['flow', '운세흐름'], + ['traits', '성향분석'], +]; + +export default function SajuMobile({ reading }) { + const [tab, setTab] = useState('basic'); + const navigate = useNavigate(); + const elementsObj = elementsByEngId(reading?.analysis_data?.element_scores); + const traits = deriveTraits(elementsObj, []); + + return ( +
+ +
+ +
+
+ + +
+ +
+
+ {TABS.map(([id, label]) => { + const active = tab === id; + return ( + + ); + })} +
+
+ +
+ {tab === 'basic' && setTab('chart')} />} + {tab === 'chart' && } + {tab === 'flow' && } + {tab === 'traits' && navigate(`/saju/today?rid=${reading?.id || ''}`)} />} +
+
+ ); +} + +function BasicTab({ reading, traits, onResult }) { + const r = reading || {}; + const rows = [ + ['생년월일', `${r.birth_year}년 ${r.birth_month}월 ${r.birth_day}일 (${r.calendar_type === 'lunar' ? '음력' : '양력'})`], + ['시간', r.birth_hour != null ? `${r.birth_hour}시` : '시간 미상'], + ['성별', r.gender === 'female' ? '여' : '남'], + ['사주', [r.saju_data?.year, r.saju_data?.month, r.saju_data?.day, r.saju_data?.hour].filter(Boolean).map((p) => `${p.stem}${p.branch}`).join(' ') || '-'], + ]; + const summary = reading?.interpretation_json?.summary || '풀이 결과를 준비 중입니다.'; + return ( +
+
+ {rows.map(([label, value], idx) => ( +
+
{label}
+
{value || '-'}
+
+ ))} +
+ + +
사주 요약
+
{summary}
+
+ +
+
+ {traits.slice(0, 5).map((t) => ())} +
+
+ +
+ + 상세 풀이 보러가기 + + +
+
+ ); +} + +function TraitChip({ ko, color }) { + // color는 'var(--el-fire)' 같은 CSS var. swatch에 직접 사용. + return ( +
+
+ {ko} +
+ ); +} + +function ChartTab({ reading, elementsObj }) { + const saju = reading?.saju_data || {}; + const ohaengArr = ['wood', 'fire', 'earth', 'metal', 'water'].map((id) => ({ + id, ko: ID_TO_KO[id], ch: ID_TO_CH[id], + value: Math.round(elementsObj?.[id] || 0), + color: ID_TO_COLOR[id], + })); + const strongest = ohaengArr.reduce((a, b) => (a.value > b.value ? a : b), { value: 0 }); + const dms = reading?.analysis_data?.day_master_strength; + return ( +
+
+
+ + 사주 명식 + 일간 중심 해석 +
+
+ {['year', 'month', 'day', 'hour'].map((pk) => ( + + ))} +
+
+ +
+
+ + 오행 분석 +
+
+ {ohaengArr.map((o) => ())} +
+ {strongest.value > 0 && ( +
+
+ {strongest.ko}({strongest.ch})의 기운이 강한 사주입니다. +
+
+ )} + {dms && ( +
+
+ 일간 강도: {dms.result} · {dms.score}점 +
+ {dms.reasons && dms.reasons.length > 0 && ( +
+ {dms.reasons.join(' · ')} +
+ )} +
+ )} +
+
+ ); +} + +const PILLAR_LABELS = { year: '년주', month: '월주', day: '일주', hour: '시주' }; + +function PillarColumn({ pillarKey, pillar, stemColor, branchColor }) { + const isDay = pillarKey === 'day'; + if (!pillar) { + return ( +
+ {PILLAR_LABELS[pillarKey]}
- +
+ ); + } + return ( +
+ {isDay && ( +
일간
+ )} +
{PILLAR_LABELS[pillarKey]}
+ + +
+
{pillar.ten_god || '-'}
+
{pillar.fortune || ''}
+
+ ); +} + +function CharBox({ char, sub, color }) { + return ( +
+
{char || '?'}
+
{sub || ''}
+
+ ); +} + +function OhaengBar({ ko, ch, value, color }) { + return ( +
+
+
{value}%
+
+
+
+ {ko}({ch}) +
+
+ ); +} + +function FlowTab({ reading }) { + const daeun = reading?.daeun_data || []; + const currentYear = new Date().getFullYear(); + const enriched = daeun.map((d) => ({ + ...d, + label: daeunLabel(d.age), + current: d.start_year <= currentYear && currentYear <= d.end_year, + })); + const current = enriched.find((x) => x.current); + return ( +
+
+
+ + 대운 흐름 + 10년 단위 +
+
+ 10년 주기로 변화하는 운의 흐름을 확인하세요. +
+
+ {enriched.map((du, i) => ())} +
+
+ {current && ( +
+
현재 대운 · {current.age}~{current.age + 9}세
+
+ {current.stem}{current.branch} · {current.label} +
+
+ {current.start_year}년 ~ {current.end_year}년 — 이 시기는 {current.label} 단계로, + 10년 간의 운기 흐름을 차분히 살펴보세요. +
+
+ )} +
+ ); +} + +function DaeunNode({ age, stem, label, current }) { + return ( +
+ {current && ( +
현재
+ )} +
{age}세
+
+ {stem} +
+
{label}
+
+ ); +} + +function TraitsTab({ traits, onToday }) { + return ( +
+
+
+ + 타고난 성향 +
+
+ {traits.map((t) => ())} +
+
+
+ + 오늘의 운세 확인하기 + + +
+
+ ); +}