feat(saju-ui-v2): home.mobile.jsx — night hero + ActionCard×3 + 입력 폼
This commit is contained in:
133
src/pages/saju/views/home.mobile.jsx
Normal file
133
src/pages/saju/views/home.mobile.jsx
Normal 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>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user