feat(saju-ui-v2): home.desktop.jsx — mt-wash 산수화 + 2-column hero

This commit is contained in:
2026-05-27 02:11:52 +09:00
parent 5acf7db27c
commit e0834b1275

View File

@@ -0,0 +1,133 @@
import React from 'react';
import { useNavigate } from 'react-router-dom';
import TitleBlock from '../_shell/TitleBlock';
import Mascot from '../_shell/Mascot';
import MascotBubble from '../_shell/MascotBubble';
import OrnateFrame from '../_shell/OrnateFrame';
import PrimaryButton from '../_shell/PrimaryButton';
import InputRow from '../_shell/InputRow';
import { IconSparkle, IconChevron, IconSun, IconHeart, IconYinYang } from '../_shell/Icons';
import useSajuForm from '../hooks/useSajuForm';
const ACTIONS = [
{ to: '/saju/today', icon: IconSun, label: '오늘의 운세', desc: '오늘 한 줄로 보는 운세', color: '#D4AF37' },
{ to: '/saju/compatibility', icon: IconHeart, label: '궁합보기', desc: '두 사람의 만남 풀이', color: '#4E6B5C' },
{ to: '/saju/result', icon: IconYinYang, label: '사주풀이', desc: '내 사주 자세히', color: '#6A4C7C' },
];
const inputStyle = {
flex: 1, padding: '8px 10px', border: '1px solid rgba(31,42,68,0.12)',
borderRadius: 8, background: '#FBF7EF', fontSize: 13, color: '#1F2A44',
fontFamily: 'inherit',
};
function pad(n) { return String(n).padStart(2, '0'); }
function dateValue(form) {
if (!form.year || !form.month || !form.day) return '';
return `${form.year}-${pad(form.month)}-${pad(form.day)}`;
}
function timeValue(form) {
if (form.hour === '' || form.hour == null) return '';
return `${pad(form.hour)}:00`;
}
export default function HomeDesktop() {
const navigate = useNavigate();
const { form, handleChange, handleSubmit, loading, error } = useSajuForm();
const onDate = (e) => {
const v = e.target.value;
if (!v) { handleChange('year', ''); handleChange('month', ''); handleChange('day', ''); return; }
const [y, m, d] = v.split('-');
handleChange('year', y);
handleChange('month', String(parseInt(m, 10)));
handleChange('day', String(parseInt(d, 10)));
};
const onTime = (e) => {
const v = e.target.value;
if (!v) { handleChange('hour', ''); return; }
const [h] = v.split(':');
handleChange('hour', String(parseInt(h, 10)));
};
return (
<main className="page mt-wash screen-in">
<div style={{ maxWidth: 1200, margin: '0 auto', padding: '48px 32px' }}>
<TitleBlock title="호령이 안내하는 사주"
subtitle="오랜 명리학 지혜와 AI 인사이트로 당신만의 길을 비춥니다." />
<div style={{
marginTop: 32, display: 'grid', gridTemplateColumns: '1fr 480px', gap: 40, alignItems: 'start',
}}>
<div>
<div style={{ display: 'flex', alignItems: 'flex-end', gap: 16 }}>
<Mascot variant="full" size={260} />
<MascotBubble tone="ivory" align="left"
text={'안녕하세요!\n저는 호령이에요.\n사주를 입력해 보실래요?'}
style={{ marginBottom: 20 }}
/>
</div>
<div style={{ marginTop: 32, display: 'grid', gap: 12 }}>
{ACTIONS.map((a) => (
<button key={a.to} onClick={() => navigate(a.to)} style={{
display: 'flex', alignItems: 'center', gap: 16,
background: '#FBF7EF', border: `1px solid ${a.color}40`,
borderRadius: 12, padding: '16px 20px', color: '#1F2A44',
fontSize: 14, fontWeight: 700, letterSpacing: '-0.01em', textAlign: 'left',
boxShadow: 'var(--shadow-card)',
}}>
<a.icon size={24} stroke={a.color} strokeWidth={1.8} />
<span style={{ flex: 1 }}>
<div style={{ fontSize: 15 }}>{a.label}</div>
<div style={{ fontSize: 12, color: '#6B6B6B', fontWeight: 500, marginTop: 2 }}>{a.desc}</div>
</span>
<IconChevron dir="right" size={16} color="#B89530" />
</button>
))}
</div>
</div>
<OrnateFrame color="#D4AF37" bg="#FBF7EF" double radius={16} padding="24px 22px">
<form onSubmit={handleSubmit}>
<div className="font-title" style={{
fontSize: 18, color: '#1F2A44', marginBottom: 12, textAlign: 'center',
}}>사주 입력</div>
<InputRow label="이름">
<input value={form.name} onChange={(e) => handleChange('name', e.target.value)}
placeholder="홍길동" style={inputStyle} />
</InputRow>
<InputRow label="생년월일">
<input type="date" value={dateValue(form)} onChange={onDate} style={inputStyle} />
</InputRow>
<InputRow label="시간">
<input type="time" value={timeValue(form)} onChange={onTime} style={inputStyle} />
</InputRow>
<InputRow label="성별">
<select value={form.gender} onChange={(e) => handleChange('gender', e.target.value)}
style={inputStyle}>
<option value="male"></option>
<option value="female"></option>
</select>
</InputRow>
<InputRow label="달력">
<select value={form.calendar_type}
onChange={(e) => handleChange('calendar_type', e.target.value)} style={inputStyle}>
<option value="solar">양력</option>
<option value="lunar">음력</option>
</select>
</InputRow>
{error && (
<div style={{ padding: '10px 14px', color: '#C04A4A', fontSize: 12 }}>{error}</div>
)}
<div style={{ padding: '14px 14px 6px' }}>
<PrimaryButton color="#6A4C7C" type="submit">
{loading ? '호령이 풀이 중...' : '내 사주 보기'}
{!loading && <IconSparkle size={12} color="#E8C76B" />}
</PrimaryButton>
</div>
</form>
</OrnateFrame>
</div>
</div>
</main>
);
}