Files
nofx/web/src/components/trader/BeginnerGuideCards.tsx
deanokk 9a80f1d88d feat: add exchange account states and refine beginner trader creation flow (#1450)
* feat: implement exchange account state management and UI updates

- Added functionality to invalidate exchange account state cache on exchange config updates, creation, and deletion.
- Introduced new API endpoint to fetch exchange account states.
- Updated frontend components to display exchange account states, including status and balance information.
- Enhanced user experience by refreshing exchange account states after relevant actions.

* feat: enhance trader creation readiness in AITradersPage and BeginnerGuideCards

---------

Co-authored-by: Dean <afei.wuhao@gmail.com>
2026-04-01 16:26:04 +08:00

212 lines
6.6 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { Brain, Landmark, Rocket, Sparkles } from 'lucide-react'
interface BeginnerGuideCardsProps {
language: string
claw402Ready: boolean
exchangeReady: boolean
strategyReady: boolean
traderReady: boolean
canCreateTrader: boolean
walletAddress?: string | null
onQuickSetupClaw402: () => void
onOpenExchange: () => void
onOpenStrategy: () => void
onCreateTrader: () => void
}
function truncateAddress(address: string) {
if (address.length <= 12) return address
return `${address.slice(0, 6)}...${address.slice(-4)}`
}
export function BeginnerGuideCards({
language,
claw402Ready,
exchangeReady,
strategyReady,
traderReady,
canCreateTrader,
walletAddress,
onQuickSetupClaw402,
onOpenExchange,
onOpenStrategy,
onCreateTrader,
}: BeginnerGuideCardsProps) {
const isZh = language === 'zh'
const cards = [
{
key: 'model',
icon: Brain,
title: isZh ? '1. 极速模型' : '1. Fast AI',
desc: isZh
? '默认就是 Claw402 + DeepSeek。第一次不用挑模型先跑起来。'
: 'Start with Claw402 + DeepSeek. No model picking needed for the first run.',
meta: walletAddress
? isZh
? `钱包 ${truncateAddress(walletAddress)}`
: `Wallet ${truncateAddress(walletAddress)}`
: isZh
? 'Base 链 USDC 按次付费'
: 'Pay per call with Base USDC',
ready: claw402Ready,
actionLabel: claw402Ready
? isZh
? '已配置'
: 'Configured'
: isZh
? '一键配置'
: 'One-click setup',
onAction: onQuickSetupClaw402,
disabled: claw402Ready,
},
{
key: 'exchange',
icon: Landmark,
title: isZh ? '2. 连接交易所' : '2. Add Exchange',
desc: isZh
? '交易所接好以后AI 才能真正下单。'
: 'Connect an exchange so the AI can actually place trades.',
meta: exchangeReady
? isZh
? '已准备好'
: 'Ready'
: isZh
? 'Binance / OKX / Bybit / Hyperliquid'
: 'Binance / OKX / Bybit / Hyperliquid',
ready: exchangeReady,
actionLabel: exchangeReady
? isZh
? '继续管理'
: 'Manage'
: isZh
? '去配置'
: 'Configure',
onAction: onOpenExchange,
disabled: false,
},
{
key: 'strategy',
icon: Sparkles,
title: isZh ? '3. 选择策略' : '3. Pick Strategy',
desc: isZh
? '先用默认策略也可以,后面再慢慢细调。'
: 'You can start with a default strategy and fine-tune later.',
meta: strategyReady
? isZh
? '已有策略可用'
: 'Strategy ready'
: isZh
? '可选,但建议提前看一眼'
: 'Optional, but worth a quick look',
ready: strategyReady,
actionLabel: isZh ? '打开策略页' : 'Open strategy',
onAction: onOpenStrategy,
disabled: false,
},
{
key: 'trader',
icon: Rocket,
title: isZh ? '4. 创建 Trader' : '4. Create Trader',
desc: isZh
? '最后一步,把模型和交易所绑在一起,就能开始运行。'
: 'Last step: bind your model and exchange, then start running.',
meta: traderReady
? isZh
? '已创建 Trader可继续添加'
: 'Trader created, you can add more'
: canCreateTrader
? isZh
? '已经可以创建'
: 'Ready to create'
: isZh
? '先完成前三步'
: 'Finish the first three steps first',
ready: traderReady,
actionLabel: traderReady
? isZh
? '继续创建'
: 'Create another'
: isZh
? '立即创建'
: 'Create now',
onAction: onCreateTrader,
disabled: !canCreateTrader,
},
]
return (
<section className="space-y-4 rounded-[28px] border border-white/10 bg-zinc-950/60 p-5 backdrop-blur-xl">
<div className="flex items-center justify-between gap-4">
<div>
<div className="text-xs font-semibold uppercase tracking-[0.3em] text-nofx-gold/80">
{isZh ? '新手引导' : 'Quickstart'}
</div>
<h2 className="mt-1 text-xl font-bold text-white">
{isZh
? '先按这 4 步走,最快上手'
: 'Follow these 4 steps to get started fast'}
</h2>
</div>
{/* <div className="rounded-full border border-white/10 bg-white/5 px-3 py-1 text-xs text-zinc-400">
{isZh ? '老手模式不会看到这块' : 'Hidden in advanced mode'}
</div> */}
</div>
<div className="grid gap-4 md:grid-cols-2 xl:grid-cols-4">
{cards.map((card) => {
const Icon = card.icon
return (
<div
key={card.key}
className="rounded-[22px] border border-white/8 bg-black/25 p-4"
>
<div className="flex items-center justify-between gap-3">
<div className="flex h-11 w-11 items-center justify-center rounded-2xl bg-white/6 text-nofx-gold">
<Icon className="h-5 w-5" />
</div>
<span
className={`rounded-full px-2.5 py-1 text-[10px] font-bold uppercase tracking-[0.22em] ${
card.ready
? 'bg-emerald-500/15 text-emerald-300'
: 'bg-zinc-800 text-zinc-400'
}`}
>
{card.ready
? isZh
? '已就绪'
: 'Ready'
: isZh
? '待完成'
: 'Pending'}
</span>
</div>
<h3 className="mt-4 text-base font-semibold text-white">
{card.title}
</h3>
<p className="mt-2 min-h-[72px] text-sm leading-6 text-zinc-400">
{card.desc}
</p>
<div className="mt-3 text-xs text-zinc-500">{card.meta}</div>
<button
type="button"
onClick={card.onAction}
disabled={card.disabled}
className={`mt-5 w-full rounded-2xl px-4 py-3 text-sm font-semibold transition ${
card.disabled
? 'cursor-not-allowed bg-zinc-900 text-zinc-500'
: 'bg-nofx-gold text-black hover:bg-yellow-400'
}`}
>
{card.actionLabel}
</button>
</div>
)
})}
</div>
</section>
)
}