feat(saju-ui-v2): today.mobile.jsx — FortuneRing + 4 ScoreCard + LuckyBox + signs
This commit is contained in:
145
src/pages/saju/views/today.mobile.jsx
Normal file
145
src/pages/saju/views/today.mobile.jsx
Normal file
@@ -0,0 +1,145 @@
|
||||
import React 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 } from '../_shell/Icons';
|
||||
|
||||
const SCORE_LABELS = {
|
||||
wealth: { ko: '재물운', icon: '財' },
|
||||
romance: { ko: '연애운', icon: '愛' },
|
||||
social: { ko: '인간관계', icon: '人' },
|
||||
career: { ko: '직장운', icon: '職' },
|
||||
};
|
||||
|
||||
export default function TodayMobile({ reading }) {
|
||||
const navigate = useNavigate();
|
||||
const scores = reading?.fortune_scores || {};
|
||||
const lucky = reading?.lucky || {};
|
||||
const signs = lucky.good_signs || [];
|
||||
const warnings = lucky.warnings || [];
|
||||
const overall = Math.round(scores.overall || 0);
|
||||
const todayStr = new Date().toLocaleDateString('ko-KR', { year: 'numeric', month: 'long', day: 'numeric' });
|
||||
|
||||
return (
|
||||
<main className="page paper-bg screen-in">
|
||||
<TopRibbon color="#D4AF37" opacity={0.7} />
|
||||
<div style={{ padding: '8px 24px 0', textAlign: 'center' }}>
|
||||
<TitleBlock title="오늘의 운세" gold="#D4AF37" subtitle={todayStr} />
|
||||
</div>
|
||||
<div style={{ padding: '14px 20px 0', display: 'flex', gap: 8, alignItems: 'flex-end' }}>
|
||||
<MascotBubble tone="ivory"
|
||||
text={'오늘 하루도\n좋은 흐름이 있어요.'}
|
||||
style={{ flex: 1, marginBottom: 8 }} />
|
||||
<Mascot variant="happy" size={130} style={{ marginRight: -8 }} />
|
||||
</div>
|
||||
|
||||
<div style={{ padding: '20px', display: 'flex', justifyContent: 'center' }}>
|
||||
<FortuneRing value={overall} />
|
||||
</div>
|
||||
|
||||
<div style={{ padding: '0 20px', display: 'grid', gridTemplateColumns: 'repeat(2, 1fr)', gap: 10 }}>
|
||||
{Object.entries(SCORE_LABELS).map(([key, { ko, icon }]) => (
|
||||
<ScoreCard key={key} ko={ko} icon={icon} value={Math.round(scores[key] || 0)} />
|
||||
))}
|
||||
</div>
|
||||
|
||||
<div style={{ padding: '20px' }}>
|
||||
<OrnateFrame color="#D4AF37" bg="#FBF7EF" radius={14} padding="16px 18px" double>
|
||||
<div style={{ display: 'flex', alignItems: 'center', gap: 6, marginBottom: 10 }}>
|
||||
<OrnamentBloom size={14} color="#D4AF37" />
|
||||
<span style={{ fontSize: 13, fontWeight: 700, color: '#1F2A44' }}>오늘의 럭키</span>
|
||||
</div>
|
||||
<div style={{ display: 'grid', gridTemplateColumns: 'repeat(3, 1fr)', gap: 12 }}>
|
||||
<LuckyItem label="색" value={Array.isArray(lucky.color) ? lucky.color.join(', ') : lucky.color} />
|
||||
<LuckyItem label="숫자" value={lucky.number} />
|
||||
<LuckyItem label="방향" value={lucky.direction} />
|
||||
</div>
|
||||
</OrnateFrame>
|
||||
</div>
|
||||
|
||||
{(signs.length > 0 || warnings.length > 0) && (
|
||||
<div style={{ padding: '0 20px 20px', display: 'grid', gap: 12 }}>
|
||||
{signs.length > 0 && <SignList title="좋은 징조" items={signs} color="#4E6B5C" />}
|
||||
{warnings.length > 0 && <SignList title="주의할 점" items={warnings} color="#C04A4A" />}
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div style={{ padding: '0 20px 40px' }}>
|
||||
<PrimaryButton color="#D4AF37" onClick={() => navigate(`/saju/result?rid=${reading?.id || ''}`)}>
|
||||
내 사주 자세히 보기 <IconChevron dir="right" size={14} color="#1F2A44" />
|
||||
</PrimaryButton>
|
||||
</div>
|
||||
</main>
|
||||
);
|
||||
}
|
||||
|
||||
function FortuneRing({ value }) {
|
||||
const R = 60;
|
||||
const C = 2 * Math.PI * R;
|
||||
const offset = C - (C * value) / 100;
|
||||
return (
|
||||
<svg width="160" height="160" viewBox="0 0 160 160">
|
||||
<circle cx="80" cy="80" r={R} stroke="#F0E9D9" strokeWidth="14" fill="none" />
|
||||
<circle cx="80" cy="80" r={R} stroke="#D4AF37" strokeWidth="14" fill="none"
|
||||
strokeDasharray={C} strokeDashoffset={offset} strokeLinecap="round"
|
||||
transform="rotate(-90 80 80)" />
|
||||
<text x="80" y="86" textAnchor="middle" className="font-title"
|
||||
style={{ fontSize: 32, fill: '#1F2A44', fontWeight: 800 }}>
|
||||
{value}<tspan style={{ fontSize: 14, fill: '#9A968D' }}>점</tspan>
|
||||
</text>
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
|
||||
function ScoreCard({ ko, icon, value }) {
|
||||
return (
|
||||
<div style={{
|
||||
background: '#FBF7EF', borderRadius: 12,
|
||||
border: '1px solid rgba(31,42,68,0.10)', boxShadow: 'var(--shadow-card)',
|
||||
padding: '12px 14px', display: 'flex', alignItems: 'center', gap: 10,
|
||||
}}>
|
||||
<div style={{
|
||||
width: 36, height: 36, borderRadius: '50%',
|
||||
background: 'rgba(212,175,55,0.12)',
|
||||
display: 'flex', alignItems: 'center', justifyContent: 'center',
|
||||
fontSize: 14, fontWeight: 800, color: '#B89530',
|
||||
}}>{icon}</div>
|
||||
<div style={{ flex: 1 }}>
|
||||
<div style={{ fontSize: 11, color: '#6B6B6B', fontWeight: 700 }}>{ko}</div>
|
||||
<div className="font-title" style={{ fontSize: 20, color: '#1F2A44' }}>
|
||||
{value}<span style={{ fontSize: 12, color: '#9A968D', fontWeight: 500 }}>점</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function LuckyItem({ label, value }) {
|
||||
return (
|
||||
<div style={{ textAlign: 'center' }}>
|
||||
<div style={{ fontSize: 11, color: '#6B6B6B', fontWeight: 700 }}>{label}</div>
|
||||
<div className="font-title" style={{ fontSize: 18, color: '#D4AF37', marginTop: 4 }}>{value || '-'}</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function SignList({ title, items, color }) {
|
||||
return (
|
||||
<div style={{
|
||||
background: '#FBF7EF', borderRadius: 12,
|
||||
border: `1px solid ${color}40`, padding: '14px 16px',
|
||||
}}>
|
||||
<div style={{ fontSize: 12, fontWeight: 700, color, marginBottom: 8 }}>{title}</div>
|
||||
<ul style={{ margin: 0, padding: 0, listStyle: 'none', display: 'grid', gap: 6 }}>
|
||||
{items.map((s, i) => (
|
||||
<li key={i} style={{ fontSize: 12.5, color: '#1F2A44', lineHeight: 1.6 }}>• {s}</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user