Files
eryao/web/src/components/AutoDivinationPage.tsx
T

146 lines
8.2 KiB
TypeScript
Raw Normal View History

import { useState, useEffect } from 'react';
2026-05-09 16:00:29 +08:00
import Icon from './Icon';
import { getPointsBalance, type PointsBalance } from '../lib/api';
2026-05-09 16:00:29 +08:00
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; guideAuto: string; shakeTitle: string; shakeBtn: string; hexPreview: string; summaryTitle: string; checkCategory: string; checkMethod: string; checkCost: string; submitBtn: string; progressLabel: string };
}
export default function AutoDivinationPage({ locale, divination: d }: Props) {
const cats = d.categories.split(',');
const [category, setCategory] = useState(cats[0]);
const [question, setQuestion] = useState('');
const [progress, setProgress] = useState(0);
const [hexLines, setHexLines] = useState<boolean[]>([]);
const [isShaking, setIsShaking] = useState(false);
const [points, setPoints] = useState<PointsBalance | null>(null);
useEffect(() => {
getPointsBalance().then(setPoints).catch(() => {});
}, []);
2026-05-09 16:00:29 +08:00
const handleShake = () => {
setIsShaking(true);
setTimeout(() => {
const newProgress = progress + 1;
setProgress(newProgress);
const line = Math.random() > 0.5;
setHexLines(prev => [...prev, line]);
setIsShaking(false);
}, 600);
};
const done = progress >= 6;
return (
<div className="flex flex-col gap-[22px] min-h-full">
<div className="flex items-center justify-between">
<div>
<h1 className="text-[#333333] text-[28px] font-bold leading-tight">{locale === 'en' ? 'Auto Cast' : d.checkMethod.replace(/^.*|^.*: /, '').replace('手动', '自动')}</h1>
<p className="text-[#666666] text-sm mt-1">{locale === 'en' ? 'Click the button or shake your device to generate six coin results.' : '点击按钮或摇动设备生成铜钱结果,连续 6 次形成完整卦象。'}</p>
</div>
<div className="hidden md:flex items-center gap-2 h-10 px-3.5 rounded-full bg-white border border-slate-200 text-[#333333] text-[13px] font-semibold">
<Icon name="paid" className="w-[18px] h-[18px] text-violet-700" />
{locale === 'en'
? `Available ${points?.availableBalance ?? '...'} credits · This time ${points?.runCost ?? 20} credits`
: `可用 ${points?.availableBalance ?? '...'} 积分 · 本次 ${points?.runCost ?? 20} 积分`}
2026-05-09 16:00:29 +08:00
</div>
</div>
<div className="flex flex-col xl:flex-row gap-[22px] min-h-0 flex-1">
{/* Left: Question + Time + Guide */}
<div className="w-full xl:w-[340px] flex flex-col gap-4 shrink-0">
<div className="bg-white rounded-2xl p-[22px] border border-slate-200 flex flex-col gap-4">
<h3 className="text-slate-900 text-lg font-bold">{d.questionTitle}</h3>
<div className="flex items-center justify-between h-[42px] px-3 rounded-[10px] bg-slate-50 border border-slate-300">
<span className="text-slate-600 text-sm">{category}</span>
<select value={category} onChange={e => setCategory(e.target.value)} className="bg-transparent text-sm outline-none cursor-pointer">
{cats.map(c => <option key={c} value={c}>{c}</option>)}
</select>
</div>
<textarea value={question} onChange={e => setQuestion(e.target.value)} placeholder={d.questionPlaceholder} rows={3}
className="w-full px-3.5 py-3 rounded-[10px] bg-white border border-slate-300 text-sm focus:outline-none focus:border-violet-400 resize-none" />
</div>
<div className="bg-white rounded-2xl p-5 border border-slate-200 flex flex-col gap-3">
<h3 className="text-slate-900 text-base font-bold">{d.timeTitle}</h3>
<div className="flex items-center justify-between h-[42px] px-3 rounded-[10px] bg-slate-50">
<input type="datetime-local" className="bg-transparent text-sm outline-none w-full" />
<Icon name="calendar_today" className="w-[18px] h-[18px] text-slate-400" />
</div>
</div>
<div className="bg-white rounded-2xl p-5 border border-slate-200 flex flex-col gap-3 flex-1 overflow-y-auto">
<h3 className="text-slate-900 text-base font-bold">{d.guideTitle}</h3>
<p className="text-slate-500 text-[13px] whitespace-pre-line">{d.guideAuto}</p>
</div>
</div>
{/* Center: Shake panel */}
<div className="flex-1 bg-white rounded-2xl p-6 border border-slate-200 flex flex-col gap-[18px]">
<div className="flex items-center justify-between">
<h3 className="text-slate-900 text-lg font-bold">{d.shakeTitle}</h3>
<span className="text-violet-600 text-[13px] font-bold">{progress} / 6</span>
</div>
{/* Coin stage */}
<div className="bg-slate-50 rounded-2xl p-[22px] flex items-center justify-center gap-6" style={{ minHeight: '194px' }}>
{[0, 1, 2].map(i => (
<div key={i} className="flex flex-col items-center gap-2" style={{ width: '86px' }}>
<img
src={isShaking ? '/images/qigua/hua.jpg' : '/images/qigua/zi.jpg'}
alt={locale === 'en' ? 'coin' : '铜钱'}
className={`w-16 h-16 rounded-full object-cover border border-amber-300 shadow-sm transition-all ${isShaking ? 'animate-pulse' : ''}`}
/>
<span className="text-slate-400 text-xs">{'铜钱'}</span>
</div>
))}
</div>
{/* Shake button */}
<div className="flex flex-col items-center gap-2.5" style={{ height: '82px', justifyContent: 'center' }}>
{!done && (
<button onClick={handleShake} disabled={isShaking}
className="flex items-center gap-2 px-8 py-2.5 rounded-full bg-violet-600 text-white text-sm font-bold hover:bg-violet-700 disabled:opacity-50 transition-colors">
<Icon name="casino" className="w-[18px] h-[18px]" />
{d.shakeBtn}
</button>
)}
{done && <p className="text-violet-600 text-sm font-medium"></p>}
</div>
{/* Hexagram preview */}
<div className="bg-white rounded-xl p-[18px] border border-slate-200 flex-1 flex flex-col gap-3 overflow-y-auto">
<p className="text-slate-900 text-base font-bold">{d.hexPreview}</p>
<div className="flex flex-col gap-2">
{hexLines.length > 0 ? hexLines.map((isYang, i) => isYang ? (
<div key={i} className="w-12 h-2 bg-violet-600 rounded" />
) : (
<div key={i} className="flex gap-1.5"><div className="w-4 h-2 bg-violet-400 rounded" /><div className="w-4 h-2 bg-violet-400 rounded" /></div>
)) : (
<p className="text-slate-300 text-sm"></p>
)}
</div>
</div>
</div>
{/* Right: Summary */}
<div className="w-full xl:w-[300px] bg-white rounded-2xl p-[22px] border border-slate-200 flex flex-col gap-[18px] shrink-0">
<h3 className="text-slate-900 text-lg font-bold">{d.summaryTitle}</h3>
<div className="bg-slate-50 rounded-xl p-4 flex flex-col gap-2" style={{ height: '94px' }}>
<p className="text-slate-500 text-[13px]">{d.progressLabel}</p>
<p className="text-violet-600 text-[28px] font-bold">{progress} / 6</p>
</div>
<p className="text-slate-500 text-sm">{d.checkCategory}</p>
<p className="text-slate-500 text-sm">{d.checkMethod}</p>
<p className="text-slate-500 text-sm">{d.checkCost}</p>
<div className="flex-1" />
<button disabled={!done}
className={`w-full h-[46px] rounded-full text-sm font-bold transition-colors ${done ? 'bg-violet-600 text-white hover:bg-violet-700' : 'bg-slate-300 text-slate-400 cursor-not-allowed'}`}>
{d.submitBtn}
</button>
</div>
</div>
</div>
);
}