feat(saju-ui-v2): home.mobile.jsx — night hero + ActionCard×3 + 입력 폼

This commit is contained in:
2026-05-27 02:10:47 +09:00
parent 76c7bcc62b
commit 5acf7db27c

View File

@@ -0,0 +1,133 @@
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 PrimaryButton from '../_shell/PrimaryButton';
import InputRow from '../_shell/InputRow';
import { IconChevron, IconSparkle, IconSun, IconHeart, IconYinYang } from '../_shell/Icons';
import useSajuForm from '../hooks/useSajuForm';
const ACTIONS = [
{ to: '/saju/today', icon: IconSun, label: '오늘의 운세', color: '#D4AF37' },
{ to: '/saju/compatibility', icon: IconHeart, label: '궁합보기', color: '#4E6B5C' },
{ to: '/saju/result', icon: IconYinYang, label: '사주풀이', 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 HomeMobile() {
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 night-bg screen-in" style={{ paddingTop: 24 }}>
<TopRibbon color="#D4AF37" opacity={0.7} />
<div style={{ padding: '8px 24px 0', textAlign: 'center', color: '#F7F2E8' }}>
<TitleBlock color="#F7F2E8" subColor="rgba(247,242,232,0.7)"
title="호령이 안내하는 사주"
subtitle="오랜 명리학 지혜와 AI 인사이트로 당신만의 길을 비춥니다."
/>
</div>
<div style={{ padding: '24px 20px 0', display: 'flex', gap: 12, alignItems: 'flex-end' }}>
<MascotBubble tone="navy" align="left"
text={'안녕하세요!\n저는 호령이에요.\n사주를 입력해 보실래요?'}
style={{ flex: 1, marginBottom: 8 }}
/>
<Mascot variant="full" size={140} style={{ marginRight: -8 }} />
</div>
<div style={{ padding: '24px 20px 0', display: 'grid', gap: 10 }}>
{ACTIONS.map((a) => (
<button key={a.to} onClick={() => navigate(a.to)} style={{
display: 'flex', alignItems: 'center', gap: 12,
background: 'rgba(247,242,232,0.06)', border: `1px solid ${a.color}55`,
borderRadius: 12, padding: '14px 16px', color: '#F7F2E8',
fontSize: 14, fontWeight: 700, letterSpacing: '-0.01em',
}}>
<a.icon size={20} stroke={a.color} strokeWidth={1.8} />
<span style={{ flex: 1, textAlign: 'left' }}>{a.label}</span>
<IconChevron dir="right" size={14} color="#E8C76B" />
</button>
))}
</div>
<div style={{ padding: '24px 20px 40px' }}>
<OrnateFrame color="#D4AF37" bg="#FBF7EF" double radius={16}>
<form onSubmit={handleSubmit}>
<div className="font-title" style={{
fontSize: 16, color: '#1F2A44', marginBottom: 8, 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>
</main>
);
}