@@ -1,4 +1,4 @@
import { useState } from 'react' ;
import { useEffect , useMemo , useState } from 'react' ;
import Icon from './Icon' ;
import Icon from './Icon' ;
interface Props {
interface Props {
@@ -7,166 +7,347 @@ interface Props {
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 } ;
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 = '字 ' | '花 ' ;
type CoinFace = 'zi ' | 'hua ' ;
type YaoType = 'youngYang' | 'youngYin' | 'oldYang' | 'oldYin' ;
function CoinImage ( { face , size = 'w-16 h-16' } : { face : CoinFace ; size? : string } ) {
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 (
return (
< img
< img
src = { face === '字 ' ? '/images/qigua/zi.jpg' : '/images/qigua/hua.jpg' }
src = { face === 'zi ' ? '/images/qigua/zi.jpg' : '/images/qigua/hua.jpg' }
alt = { face }
alt = { face === 'zi' ? '字' : '花' }
className = { ` ${ size } rounded-full object-cover border border-amber-300 shadow-sm ` }
className = { ` h-20 w-20 rounded-full object-cover shadow-sm transition-transform ${ selected ? 'ring-2 ring-violet-600 ring-offset-2 ring-offset-slate-50' : '' } ` }
draggable = { false }
/ >
/ >
) ;
) ;
}
}
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 < div className = { ` h-2.5 w-full rounded-full ${ color } ` } / > ;
}
return (
< div className = "flex h-2.5 w-full gap-4" >
< div className = { ` h-2.5 flex-1 rounded-full ${ color } ` } / >
< div className = { ` h-2.5 flex-1 rounded-full ${ color } ` } / >
< / div >
) ;
}
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 ) {
export default function ManualDivinationPage ( { locale , divination : d } : Props ) {
const cats = d . categories . split ( ',' ) ;
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 [ category , setCategory ] = useState ( cats [ 0 ] ) ;
const [ question , setQuestion ] = useState ( '' ) ;
const [ question , setQuestion ] = useState ( text . defaultQuestion ) ;
const [ yaoIndex , setYaoIndex ] = useState ( 0 ) ;
const [ selectedTime , setSelectedTime ] = useState ( ( ) = > formatDateTimeInput ( new Date ( ) ) ) ;
const [ coins , setCoins ] = useState < [ CoinFace , CoinFace , CoinFace ] > ( [ '字 ' , '字 ' , '字 ' ] ) ;
const [ coins , setCoins ] = useState < [ CoinFace , CoinFace , CoinFace ] > ( [ 'zi ' , 'zi ' , 'zi ' ] ) ;
const [ yaoResults , setYaoResults ] = useState < CoinFace [ ] [ ] > ( [ ] ) ;
const [ yaoResults , setYaoResults ] = useState < YaoType [ ] > ( [ ] ) ;
const [ guideStep , setGuideStep ] = useState < number | null > ( 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 ) = > {
const flipCoin = ( idx : number ) = > {
const next : [ CoinFace , CoinFace , CoinFace ] = [ . . . coins ] ;
setCoins ( ( current ) = > {
next [ idx ] = next [ idx ] === '字' ? '花' : '字' ;
const next = [ . . . current ] as [ CoinFace , CoinFace , CoinFace ] ;
setCoins ( next ) ;
next [ idx ] = next [ idx ] === 'zi' ? 'hua' : 'zi' ;
return next ;
} ) ;
} ;
} ;
const confirmYao = ( ) = > {
const confirmYao = ( ) = > {
const newResults = [ . . . yaoResults , [ . . . coins ] ] ;
if ( progress > = TOTAL_YAO_COUNT ) return ;
setYaoResults ( newResults ) ;
setYaoResults ( ( current ) = > [ . . . current , currentYaoType ] ) ;
if ( newResults . length < 6 ) {
setCoins ( [ 'zi' , 'zi' , 'zi' ] ) ;
setYaoIndex ( newResults . length ) ;
setCoins ( [ '字' , '字' , '字' ] ) ;
}
} ;
} ;
const progress = yaoResults . length ;
const showPreviousGuide = ( ) = > setGuideStep ( ( step ) = > ( step === null ? 0 : Math.max ( step - 1 , 0 ) ) ) ;
const isYang = ( c : CoinFace [ ] ) = > c . filter ( x = > x === '字' ) . length % 2 === 1 ;
const showNextGuide = ( ) = > setGuideStep ( ( step ) = > ( step === null ? 0 : Math.min ( step + 1 , text . guideSteps . length - 1 ) ) ) ;
const yaoNames = [ '初爻' , '二爻' , '三爻' , '四爻' , '五爻' , '上爻' ] ;
return (
return (
< div className = "flex flex-col gap-[22px] min-h-full " >
< div className = "relative flex min-h-full flex-col gap-[22px]" >
< div className = "flex items-center justify-between" >
< div className = "flex items-center justify-between gap-5 " >
< div >
< div className = "min-w-0" >
< h1 className = "text-[#333333] text-[28px] font-bold leading-tight" > { locale === 'en' ? 'Manual Cast' : d . checkMethod . replace ( /^.*: |^.*: / , '' ) } < / h1 >
< h1 className = "text-[28px] font-bold leading-tight text-[#333333]" > { text . title } < / h1 >
< p className = "text-[#666666] text-sm mt-1" > { locale === 'en' ? 'Prepare three matching coins and enter six results from bottom line to top line.' : '准备三枚相同的钱币,从初爻到上爻依次录入六次结果。' } < / p >
< p className = "mt-1 text-sm text-[#666666]" > { text . subtitle } < / p >
< / div >
< / 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" >
< div className = "hidden h-10 items-center gap-2 rounded-full border border-slate-200 bg-white px-3.5 text-[13px] font-semibold text-[#333333] md:flex " >
< Icon name = "paid" className = "w -[18px] h -[18px] text-violet-700" / >
< Icon name = "paid" className = "h -[18px] w -[18px] text-violet-700" / >
{ locale === 'en' ? 'Available 120 credits · This time 20 credits' : '可用 120 积分 · 本次 20 积分' }
{ text . balance }
< / div >
< / div >
< / div >
< / div >
< div className = "flex flex-col xl:flex-row gap-[22px] min-h-0 flex-1" >
{ /* Left: Question + Time + Guide */ }
< div className = "flex min-h-0 flex-1 flex-col gap-[22px] xl:flex-row" >
< div className = "w-full xl:w-[360px] flex flex-col gap-4 shrink-0 " >
< div className = "flex w-full shrink-0 flex-col gap-4 xl:w-[360px] " >
{ /* Question panel */ }
< section className = "flex h-[300px] flex-col gap-4 rounded-2xl border border-slate-200 bg-white p-[22px]" >
< div className = "bg-white rounded-2xl p-[22px] border border -slate-2 00 flex flex-col gap-4" style = { { height : '300px' } } >
< h2 className = "text-lg font-bold text -slate-9 00" > { d . questionTitle } < / h2 >
< h3 className = "text-slate-900 text-lg font-bold " > { d . questionTitle } < / h3 >
< label className = "sr-only" htmlFor = "manual-category "> { d . categoryLabel } < / label >
< div className = "flex items-center justify-between h-[42px] px-3 rounded-[10px] bg-slate-50 border border-slate-300" >
< select
< span className = "text-slate-600 text-sm" > { category } < / span >
id = "manual-category"
< select value = { category } onChange = { e = > setCategory ( e . target . value ) } className = "bg-transparent text-sm outline-none cursor-pointer" >
value = { category }
{ cats . map ( c = > < option key = { c } value = { c } > { c } < / option > ) }
onChange = { ( event ) = > setCategory ( event . target . value ) }
className = "h-[42px] rounded-[10px] border border-slate-300 bg-slate-50 px-3 text-sm font-bold text-[#333333] outline-none focus:border-violet-500"
>
{ cats . map ( ( cat ) = > < option key = { cat } value = { cat } > { cat } < / option > ) }
< / select >
< / select >
< textarea
value = { question }
onChange = { ( event ) = > setQuestion ( event . target . value ) }
placeholder = { d . questionPlaceholder }
className = "min-h-0 flex-1 resize-none rounded-[10px] border border-slate-300 bg-white px-3.5 py-3 text-sm text-[#333333] outline-none focus:border-violet-500"
/ >
< / section >
< section className = { ` flex h-[132px] flex-col gap-3 rounded-2xl border bg-white p-5 ${ guideStep === 1 ? 'border-violet-500 ring-4 ring-violet-100' : 'border-slate-200' } ` } >
< h2 className = "text-base font-bold text-slate-900" > { d . timeTitle } < / h2 >
< div className = "flex h-[42px] items-center justify-between gap-3 rounded-[10px] bg-slate-50 px-3" >
< input
type = "datetime-local"
value = { selectedTime }
onChange = { ( event ) = > setSelectedTime ( event . target . value ) }
className = "w-full bg-transparent text-sm font-semibold text-[#333333] outline-none"
/ >
< span className = "shrink-0 text-[13px] font-bold text-violet-700" > { text . modify } < / span >
< / div >
< / div >
< textarea value = { question } onChange = { e = > setQuestion ( e . target . value ) } placeholder = { d . questionPlaceholder }
< / section >
className = "flex-1 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 >
< section className = { ` flex h-[214px] flex-col gap-3 rounded-2xl border bg-white p-5 ${ guideStep === 0 ? 'border-violet-500 ring-4 ring-violet-100' : 'border-slate-200' } ` } >
{ /* Time panel */ }
< h2 className = "text-base font-bold text-slate-900" > { d . guideTitle } < / h2 >
< div className = "bg-white rounded-2xl p-5 border border-slate-200 flex flex-col gap-3" style = { { height : '132px' } } >
{ text . guideLines . map ( ( line ) = > < p key = { line } className = "text-[13px] leading-relaxed text-[#666666]" > { line } < / p > ) }
< h3 className = "text-slate-900 text-base font-bold" > { d . timeTitle } < / h3 >
< button
< div className = "flex items-center justify-between h-[42px] px-3 rounded-[10px] bg-slate-50" >
type = "button"
< input type = "datetime-local" className = "bg-transparent text-sm outline-none w-full" / >
onClick = { ( ) = > setGuideStep ( 0 ) }
< Icon name = "calendar_today" className = "w-[18px] h -[18 px] text-slate-400" / >
className = "mt-auto flex h-8 w-fit items-center gap-2 rounded-full bg-violet-50 px-3 text -[13 px] font-bold text-violet-700"
< / div >
>
< / div >
< Icon name = "notifications" className = "h-[18px] w-[18px]" / >
{ /* Guide panel */ }
{ text . openGuide }
< div className = "bg-white rounded-2xl p-5 border border-slate-200 flex flex-col gap-3 flex-1 overflow-y-auto" style = { { height : '214px' } } >
< / button >
< h3 className = "text-slate-900 text-base font-bold" > { d . guideTitle } < / h3 >
< / section >
< p className = "text-slate-500 text-[13px] whitespace-pre-line" > { d . guideManual } < / p >
< / div >
< / div >
< / div >
{ /* Center: Yao panel */ }
< section className = { ` flex min-w-0 flex-1 flex-col gap-4 rounded-2xl border bg-white p-6 ${ guideStep === 2 ? 'border-violet-500 ring-4 ring-violet-100' : 'border-slate-200' } ` } >
< div className = "flex-1 bg-white rounded-2xl p-6 border border-slate-200 flex flex-col gap-4 overflow-y-auto " >
< div className = "flex items-center justify-between gap-4 " >
< div className = "flex items-center justify-between" >
< h2 className = "text-lg font-bold text-slate-900" > { d . yaoTitle } < / h2 >
< h3 className = "text-slate-900 text-lg font-bold" > { d . yaoTitle } < / h3 >
< span className = "text-[13px] font-bold text-violet-700" > { progress } / { TOTAL_YAO_COUNT } < / span >
< span className = "text-violet-600 text-[13px] font-bold" > { progress } / 6 < / span >
< / div >
< / div >
{ /* 6 Yao rows - from bottom to top (上爻 at top) */ }
< div className = "flex flex-col gap-2.5" >
< div className = "flex flex-col gap-2.5" >
{ [ 5 , 4 , 3 , 2 , 1 , 0 ] . map ( ( i ) = > {
{ [ 5 , 4 , 3 , 2 , 1 , 0 ] . map ( ( index ) = > {
const result = yaoResults [ i ] ;
const result = yaoResults [ index ] ;
const isCurrent = i === yaoIndex && progress < 6 ;
const active = index === progress && progress < TOTAL_YAO_COUNT ;
const isDone = result !== undefined ;
return (
return (
< div key = { i }
< div
className = { ` flex items-center gap-4 h-[62px] px-3.5 rounded-[10px] ${ isCurrent ? 'bg-violet-50 border border-violet-400' : isDone ? 'bg-slate-50' : 'bg-slate-50 border border-slate-200' } ` } >
key = { index }
< span className = "text-slate-400 text-xs font-medium w-8" > { yaoNames [ i ] } < / span >
className = { ` flex h-[62px] items-center gap-4 rounded-[10px] px-3.5 ${ active ? 'border border-violet-600 bg-violet-50' : result ? 'border border-slate-200 bg-white' : 'bg-slate-50' } ` }
{ isDone ? (
>
< div className = "flex gap-2" >
< span className = { ` w-16 text-sm font-bold ${ active || result ? 'text-violet-700' : 'text-slate-400' } ` } > { text . lineNames [ index ] } < / span >
{ result . map ( ( face , ci ) = > (
< div className = "min-w-0 flex-1" >
< CoinImage key = { ci } face = { face } size = "w-8 h-8" / >
< YaoGlyph type = { result } active = { active } / >
) ) }
< / div >
< / div >
) : (
< span className = { ` w-20 text-right text-[13px] font-bold ${ result ? 'text-violet-700' : 'text-slate-400' } ` } >
< span className = "text-slate-300 text-xs" > 待 录 入 < / span >
{ result ? text . yaoTypeNames [ result ] : text . pending }
) }
< / span >
{ isDone && (
< div className = "ml-auto" >
{ isYang ( result ) ? (
< div className = "w-12 h-2 bg-violet-600 rounded" / >
) : (
< div 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 >
) }
< / div >
) }
< / div >
< / div >
) ;
) ;
} ) }
} ) }
< / div >
< / div >
{ /* Coin selector */ }
{ progress < TOTAL_YAO_COUNT && (
{ progress < 6 && (
< >
< div className = "bg-slate-50 rounded-xl p-4 flex flex-col items-center gap-4" style = { { minHeight : '142px' } } >
< div className = "flex min-h-[142px] items-center justify-center rounded-xl bg-slate-50 p-4" >
< div className = "flex items-center gap-6" >
< div className = "flex items-center justify-center gap-6" >
{ coins . map ( ( face , ci ) = > (
{ coins . map ( ( face , index ) = > (
< div key = { ci } className = "flex flex-col items-center gap-2" onClick = { ( ) = > flipCoin ( ci ) } style = { { cursor : 'pointer' , width : '86px' } } >
< button
< div className = { ` w-16 h-16 rounded-full border-2 flex items-center justify-center text-lg font-bold transition-all cursor-pointer select-none ${ face === '字' ? 'bg-amber-100 border-amber-400 text-amber-700' : 'bg-slate-200 border-slate-400 text-slate-600' } ` } >
key = { index }
< CoinImage face = { face } / >
type = "button"
< / div >
onClick = { ( ) = > flipCoin ( index ) }
< span className = "text-slate-400 text-xs" > { face === '字' ? '正面' : '反面' } < / span >
className = "flex w-20 flex-col items-center gap-2 text-[13px] font-bold text-slate-600"
< / div >
>
< CoinImage face = { face } selected = { face === 'hua' } / >
< span > { face === 'zi' ? text.zi : text.hua } < / span >
< / button >
) ) }
) ) }
< / div >
< / div >
< / div >
< / div >
) }
{ progress < 6 && (
< div className = "rounded-xl border border-violet-100 bg-violet-50 px-4 py-3 text-center text-[13px] font-semibold text-violet-800" >
< button onClick = { confirmYao } className = "w-full h-10 rounded-xl bg-violet-600 text-white text-[13px] font-bold hover:bg-violet-700 transition-colors" > { d . confirmBtn } < / button >
{ text . yaoTypeNames [ currentYaoType ] }
) }
< / div >
< / div >
{ /* Right: Summary */ }
< button
< div className = "w-full xl:w-[300px] bg-white rounded-2xl p-[22px] border border-slate-200 flex flex-col gap-[18px] shrink-0" >
type = "button"
< h3 className = "text-slate-900 text-lg font-bold" > { d . summaryTitle } < / h3 >
onClick = { confirmYao }
{ /* Progress */ }
className = "h-10 w-full rounded-full bg-violet-700 text-[13px] font-bold text-white transition-colors hover:bg-violet-800"
< div className = "bg-slate-50 rounded-xl p-4 flex flex-col gap-2" >
>
< p className = "text-slate-500 text-[13px]" > { d . progressLabel } < / p >
{ d . confirmBtn }
< p className = "text-violet-600 text-[28px] font-bold" > { progress } / 6 < / p >
< / button >
< / >
) }
< / section >
< aside className = "flex w-full shrink-0 flex-col gap-[18px] rounded-2xl border border-slate-200 bg-white p-[22px] xl:w-[300px]" >
< h2 className = "text-lg font-bold text-slate-900" > { d . summaryTitle } < / h2 >
< div className = "flex h-[94px] flex-col gap-2 rounded-xl bg-slate-50 p-4" >
< p className = "text-[13px] text-[#666666]" > { d . progressLabel } < / p >
< p className = "text-[28px] font-bold text-violet-700" > { progress } / { TOTAL_YAO_COUNT } < / p >
< / div >
< / div >
< p className = "text-slate-500 text-sm" > { d . checkCategory } < / p >
< p className = "text-sm text-[#666666]" > { text . questionTypePrefix } { locale === 'en' ? ': ' : ': ' } { category } < / p >
< p className = "text-slate-500 text-sm " > { d . checkMethod } < / p >
< p className = "text-sm text-[#666666] " > { text . method } < / p >
< p className = "text-slate-500 text-sm " > { d . checkCost } < / p >
< p className = "text-sm text-[#666666] " > { d . checkCost } < / p >
< div className = "flex-1" / >
< div className = "flex-1" / >
< button disabled = { progress < 6 }
< button
className = { ` w-full h-[46px] rounded-full text-sm font-bold transition-colors ${ progress >= 6 ? 'bg-violet-600 text-white hover:bg-violet-700' : 'bg-slate-300 text-slate-400 cursor-not-allowed' } ` } >
type = "button"
{ d . submitBtn }
disabled = { progress < TOTAL_YAO_COUNT }
className = { ` h-[46px] w-full rounded-full text-sm font-bold transition-colors ${ progress >= TOTAL_YAO_COUNT ? 'bg-violet-700 text-white hover:bg-violet-800' : 'cursor-not-allowed bg-slate-300 text-slate-400' } ${ guideStep === 3 ? 'ring-4 ring-violet-100' : '' } ` }
>
{ text . submit }
< / button >
< / aside >
< / div >
{ guideOpen && guide && (
< div className = "fixed inset-0 z-50 flex items-center justify-center bg-black/70 p-5" >
< div className = "w-full max-w-md rounded-2xl border border-white/10 bg-slate-950 p-6 text-white shadow-2xl" >
< div className = "mb-4 flex items-center justify-between gap-4" >
< span className = "text-sm font-bold text-violet-200" > { guideStep + 1 } / { text . guideSteps . length } < / span >
< button type = "button" onClick = { ( ) = > setGuideStep ( null ) } className = "rounded-full p-1 text-white/70 hover:bg-white/10 hover:text-white" >
< Icon name = "close" className = "h-5 w-5" / >
< / button >
< / button >
< / div >
< / div >
< h2 className = "text-xl font-bold" > { guide [ 0 ] } < / h2 >
< p className = "mt-3 text-sm leading-6 text-white/80" > { guide [ 1 ] } < / p >
< div className = "mt-6 flex items-center justify-between gap-3" >
< button type = "button" onClick = { showPreviousGuide } disabled = { guideStep === 0 } className = "h-10 rounded-full px-4 text-sm font-bold text-white/70 disabled:opacity-40" >
{ text . prevGuide }
< / button >
{ guideStep === text . guideSteps . length - 1 ? (
< button type = "button" onClick = { ( ) = > setGuideStep ( null ) } className = "h-10 rounded-full bg-white px-5 text-sm font-bold text-slate-950" >
{ text . closeGuide }
< / button >
) : (
< button type = "button" onClick = { showNextGuide } className = "h-10 rounded-full bg-white px-5 text-sm font-bold text-slate-950" >
{ text . nextGuide }
< / button >
) }
< / div >
< / div >
< / div >
< / div >
< / div >
) }
< / div >
) ;
) ;
}
}