Initial commit: tkmind admin frontend (React + Vite).
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -0,0 +1,122 @@
|
||||
import { useState } from 'react';
|
||||
import { Link, Navigate, useParams } from 'react-router-dom';
|
||||
import { rechargeUser, updateAdminUser } from '../../api/client';
|
||||
import { CapabilitySettings } from '../../components/CapabilitySettings';
|
||||
import { PolicySettings } from '../../components/PolicySettings';
|
||||
import { SkillSettings } from '../../components/SkillSettings';
|
||||
import { useAdminUsers } from '../hooks/useAdminUsers';
|
||||
import { formatYuan } from '../utils/format';
|
||||
|
||||
export function UserDetailPage() {
|
||||
const { userId = '' } = useParams();
|
||||
const { users, loading, error, reload, setError } = useAdminUsers();
|
||||
const [message, setMessage] = useState<string | null>(null);
|
||||
const [recharge, setRecharge] = useState({ amountYuan: '10', note: '' });
|
||||
|
||||
const user = users.find((row) => row.id === userId);
|
||||
|
||||
const handleRecharge = async (e: React.FormEvent) => {
|
||||
e.preventDefault();
|
||||
if (!user) return;
|
||||
setMessage(null);
|
||||
setError(null);
|
||||
try {
|
||||
const amountCents = Math.round(Number(recharge.amountYuan) * 100);
|
||||
await rechargeUser(user.id, amountCents, recharge.note || undefined);
|
||||
setMessage('充值成功');
|
||||
setRecharge({ amountYuan: '10', note: '' });
|
||||
await reload();
|
||||
} catch (err) {
|
||||
setError(err instanceof Error ? err.message : '充值失败');
|
||||
}
|
||||
};
|
||||
|
||||
if (!loading && !user) {
|
||||
return <Navigate to="/admin/users" replace />;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="admin-page">
|
||||
<div className="admin-page-head">
|
||||
<Link to="/admin/users" className="ghost-btn admin-back-inline">
|
||||
← 返回用户列表
|
||||
</Link>
|
||||
<h2>{user?.displayName ?? '用户详情'}</h2>
|
||||
{user && <p className="muted">@{user.username} · {user.role} · {user.status}</p>}
|
||||
</div>
|
||||
|
||||
{error && <p className="banner banner-error">{error}</p>}
|
||||
{message && <p className="banner banner-info">{message}</p>}
|
||||
|
||||
{loading || !user ? (
|
||||
<p className="muted">加载中…</p>
|
||||
) : (
|
||||
<>
|
||||
<section className="admin-card">
|
||||
<div className="admin-card-head">
|
||||
<h2>账户信息</h2>
|
||||
{user.role === 'user' && (
|
||||
<button
|
||||
type="button"
|
||||
className="ghost-btn"
|
||||
onClick={() =>
|
||||
void updateAdminUser(user.id, {
|
||||
status: user.status === 'active' ? 'disabled' : 'active',
|
||||
}).then(reload)
|
||||
}
|
||||
>
|
||||
{user.status === 'active' ? '禁用' : '启用'}
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
<dl className="admin-dl">
|
||||
<div>
|
||||
<dt>余额</dt>
|
||||
<dd>¥{formatYuan(user.balanceCents)}</dd>
|
||||
</div>
|
||||
<div>
|
||||
<dt>工作目录</dt>
|
||||
<dd className="mono">{user.workspaceRoot}</dd>
|
||||
</div>
|
||||
</dl>
|
||||
</section>
|
||||
|
||||
{user.role === 'user' && (
|
||||
<>
|
||||
<section className="admin-card">
|
||||
<h2>充值</h2>
|
||||
<form className="admin-form" onSubmit={handleRecharge}>
|
||||
<input
|
||||
placeholder="金额(元)"
|
||||
value={recharge.amountYuan}
|
||||
onChange={(e) => setRecharge((s) => ({ ...s, amountYuan: e.target.value }))}
|
||||
/>
|
||||
<input
|
||||
placeholder="备注"
|
||||
value={recharge.note}
|
||||
onChange={(e) => setRecharge((s) => ({ ...s, note: e.target.value }))}
|
||||
/>
|
||||
<button type="submit" className="send-btn">
|
||||
充值
|
||||
</button>
|
||||
</form>
|
||||
</section>
|
||||
|
||||
<CapabilitySettings users={users} userId={user.id} userOnly />
|
||||
<SkillSettings users={users} userId={user.id} userOnly />
|
||||
<PolicySettings users={users} userId={user.id} userOnly />
|
||||
</>
|
||||
)}
|
||||
|
||||
{user.role === 'admin' && (
|
||||
<section className="admin-card">
|
||||
<p className="muted">
|
||||
管理员账号拥有 goose 全权限:不受能力、技能、策略、计费与路径限制,会话以 auto 模式运行。
|
||||
</p>
|
||||
</section>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user