import { useEffect, useMemo, useState } from 'react'; import Icon from './Icon'; interface Props { locale: string; dashboard: { brandName: string; navHome: string; navStore: string; navDivination: string; navManual: string; navAuto: string; navHistory: string; navLanguage: string; navSettings: string; logout: string }; divination: { questionTitle: string; questionPlaceholder: string; categoryLabel: string; categories: string; timeTitle: string; timeHint: string; guideTitle: string; guideManual: string; yaoTitle: string; coinLabel: string; confirmBtn: string; summaryTitle: string; checkCategory: string; checkMethod: string; checkCost: string; submitBtn: string; progressLabel: string }; } type CoinFace = 'zi' | 'hua'; type YaoType = 'youngYang' | 'youngYin' | 'oldYang' | 'oldYin'; const TOTAL_YAO_COUNT = 6; function formatDateTimeInput(value: Date): string { const pad = (n: number) => String(n).padStart(2, '0'); return `${value.getFullYear()}-${pad(value.getMonth() + 1)}-${pad(value.getDate())}T${pad(value.getHours())}:${pad(value.getMinutes())}`; } function fromHuaCount(huaCount: number): YaoType { switch (huaCount) { case 0: return 'oldYin'; case 1: return 'youngYang'; case 2: return 'youngYin'; case 3: return 'oldYang'; default: throw new RangeError('huaCount must be 0..3'); } } function fromCoins(coins: CoinFace[]): YaoType { return fromHuaCount(coins.filter((coin) => coin === 'hua').length); } function CoinImage({ face, selected }: { face: CoinFace; selected?: boolean }) { return ( {face ); } function YaoGlyph({ type, active }: { type?: YaoType; active?: boolean }) { const color = active || type ? 'bg-violet-700' : 'bg-slate-200'; if (!type || type === 'youngYang' || type === 'oldYang') { return
; } return (
); } const copy = { zh: { title: '手动起卦', subtitle: '准备三枚相同的钱币,从初爻到上爻依次录入六次结果。', balance: '可用 120 积分 · 本次 20 积分', defaultQuestion: '我接下来三个月的事业发展需要注意什么?', modify: '修改', guideLines: ['从初爻开始,按从下往上的顺序记录。', '每一爻由三枚钱币的字面/花面组合决定。', '六爻完成后才可开始解卦。'], openGuide: '查看手动起卦教程', guideSteps: [ ['手动起卦', '准备三枚相同的钱币。每次记录一爻,按从下往上的顺序共记录六爻。'], ['确认时间', '先确认起卦时间。如需调整,点击右侧「修改」。'], ['依次录入六爻', '从初爻开始逐条选择,未完成前下一爻不可点。每条会弹出三枚钱币选择面板。'], ['开始分析', '六爻都填完后,「开始解卦」按钮会高亮提示,点击即可解卦。'], ], closeGuide: '结束教程', nextGuide: '下一步', prevGuide: '上一步', lineNames: ['初爻', '二爻', '三爻', '四爻', '五爻', '上爻'], pending: '待录入', zi: '字', hua: '花', yaoTypeNames: { youngYang: '少阳', youngYin: '少阴', oldYang: '老阳', oldYin: '老阴' }, questionTypePrefix: '问题类型', method: '起卦方式:手动起卦', submit: '开始解卦', }, zh_Hant: { title: '手動起卦', subtitle: '準備三枚相同的錢幣,從初爻到上爻依序錄入六次結果。', balance: '可用 120 積分 · 本次 20 積分', defaultQuestion: '我接下來三個月的事業發展需要注意什麼?', modify: '修改', guideLines: ['從初爻開始,按從下往上的順序記錄。', '每一爻由三枚錢幣的字面/花面組合決定。', '六爻完成後才可開始解卦。'], openGuide: '查看手動起卦教程', guideSteps: [ ['手動起卦', '準備三枚相同的錢幣。每次記錄一爻,按從下往上的順序共記錄六爻。'], ['確認時間', '先確認起卦時間。如需調整,點擊右側「修改」。'], ['依序錄入六爻', '從初爻開始逐條選擇,未完成前下一爻不可點擊。每條會彈出三枚錢幣選擇面板。'], ['開始分析', '六爻都填完後,「開始解卦」按鈕會高亮提示,點擊即可解卦。'], ], closeGuide: '結束教程', nextGuide: '下一步', prevGuide: '上一步', lineNames: ['初爻', '二爻', '三爻', '四爻', '五爻', '上爻'], pending: '待錄入', zi: '字', hua: '花', yaoTypeNames: { youngYang: '少陽', youngYin: '少陰', oldYang: '老陽', oldYin: '老陰' }, questionTypePrefix: '問題類型', method: '起卦方式:手動起卦', submit: '開始解卦', }, en: { title: 'Manual Casting', subtitle: 'Prepare three identical coins and record six results from the first yao at the bottom to the top yao.', balance: 'Available 120 credits · This reading 20 credits', defaultQuestion: 'What should I pay attention to in my career development over the next three months?', modify: 'Modify', guideLines: ['Record from the first yao upward.', 'Each yao is determined by the text-side and flower-side combination of three coins.', 'Start interpretation after all six yao are complete.'], openGuide: 'View Manual Casting Guide', guideSteps: [ ['Manual Casting', 'Prepare three identical coins. Record one yao at a time, from bottom to top, until all six yao are complete.'], ['Confirm Time', 'Check the casting time first. Use Modify on the right if you need to adjust it.'], ['Fill Six Yao in Order', 'Start from the first yao and complete one row at a time. The next row stays locked until the current row is confirmed.'], ['Start Interpretation', 'After all six yao are filled, Start Interpretation becomes active. Select it to continue.'], ], closeGuide: 'Finish', nextGuide: 'Next', prevGuide: 'Back', lineNames: ['First Yao', 'Second Yao', 'Third Yao', 'Fourth Yao', 'Fifth Yao', 'Top Yao'], pending: 'Pending', zi: 'Text', hua: 'Flower', yaoTypeNames: { youngYang: 'Young Yang', youngYin: 'Young Yin', oldYang: 'Old Yang', oldYin: 'Old Yin' }, questionTypePrefix: 'Category', method: 'Method: Manual Casting', submit: 'Start Interpretation', }, } as const; export default function ManualDivinationPage({ locale, divination: d }: Props) { const text = copy[locale as keyof typeof copy] ?? copy.zh; const cats = useMemo(() => d.categories.split(','), [d.categories]); const [category, setCategory] = useState(cats[0]); const [question, setQuestion] = useState(text.defaultQuestion); const [selectedTime, setSelectedTime] = useState(() => formatDateTimeInput(new Date())); const [coins, setCoins] = useState<[CoinFace, CoinFace, CoinFace]>(['zi', 'zi', 'zi']); const [yaoResults, setYaoResults] = useState([]); const [guideStep, setGuideStep] = useState(null); useEffect(() => { setCategory(cats[0]); }, [cats]); const progress = yaoResults.length; const currentYaoType = fromCoins(coins); const guideOpen = guideStep !== null; const guide = guideOpen ? text.guideSteps[guideStep] : null; const flipCoin = (idx: number) => { setCoins((current) => { const next = [...current] as [CoinFace, CoinFace, CoinFace]; next[idx] = next[idx] === 'zi' ? 'hua' : 'zi'; return next; }); }; const confirmYao = () => { if (progress >= TOTAL_YAO_COUNT) return; setYaoResults((current) => [...current, currentYaoType]); setCoins(['zi', 'zi', 'zi']); }; const showPreviousGuide = () => setGuideStep((step) => (step === null ? 0 : Math.max(step - 1, 0))); const showNextGuide = () => setGuideStep((step) => (step === null ? 0 : Math.min(step + 1, text.guideSteps.length - 1))); return (

{text.title}

{text.subtitle}

{text.balance}

{d.questionTitle}