fix: render admin password reset as modal

This commit is contained in:
FengLee
2026-06-06 22:26:01 +08:00
parent fd18f2de68
commit a687958a9d
2 changed files with 65 additions and 33 deletions

View File

@@ -31,6 +31,17 @@ await runTest('admin user management exposes reset password without hiding it be
assert.match(source, /onClick=\{\(\) => startResetPassword\(editingUser\)\}/);
});
await runTest('admin reset password form is rendered as an overlay dialog', () => {
const source = read('src/components/admin/user-management-tab.tsx');
const resetSection = source.slice(source.indexOf('{resetPwUser && ('), source.indexOf('{editingUser && ('));
assert.match(resetSection, /fixed inset-0 z-50/);
assert.match(resetSection, /max-h-\[90vh\] overflow-y-auto/);
assert.doesNotMatch(resetSection, /\{resetPwUser && \(\s*<Card className="border-primary\/30">/);
assert.match(source, /setRechargeUser\(null\)/);
assert.match(source, /setShowAddForm\(false\)/);
});
await runTest('admin password reset upserts auth credentials instead of silently updating zero rows', () => {
const source = read('src/lib/admin-users-service.ts');

View File

@@ -221,6 +221,9 @@ export default function UserManagementTab() {
const startEdit = (user: ManagedUser) => {
setEditingUser(user);
setResetPwUser(null);
setRechargeUser(null);
setShowAddForm(false);
setEditRole(user.role); setEditTier(user.membershipTier);
setEditEmail(user.email || '');
setEditCredits(String(user.creditsBalance)); setEditQuota(String(user.dailyQuotaLimit));
@@ -232,6 +235,8 @@ export default function UserManagementTab() {
setResetPwUser(user);
setNewPassword('');
setEditingUser(null);
setRechargeUser(null);
setShowAddForm(false);
};
const handleSaveEdit = async () => {
@@ -289,6 +294,9 @@ export default function UserManagementTab() {
setRechargeAmount('');
setRechargeReason('管理员手动充值');
setRechargeMode('add');
setEditingUser(null);
setResetPwUser(null);
setShowAddForm(false);
};
const handleRecharge = async () => {
@@ -545,7 +553,16 @@ export default function UserManagementTab() {
</Button>
</div>
{activeView === 'users' && (
<Button size="sm" className="gap-1.5" onClick={() => setShowAddForm(true)}>
<Button
size="sm"
className="gap-1.5"
onClick={() => {
setShowAddForm(true);
setEditingUser(null);
setResetPwUser(null);
setRechargeUser(null);
}}
>
<Plus className="h-4 w-4" />
</Button>
)}
@@ -847,39 +864,43 @@ export default function UserManagementTab() {
{/* Reset Password Form */}
{resetPwUser && (
<Card className="border-primary/30">
<CardHeader>
<div className="flex items-center justify-between">
<div>
<CardTitle className="text-lg flex items-center gap-2">
<KeyRound className="h-5 w-5 text-primary" />
- {resetPwUser.nickname}
</CardTitle>
<CardDescription>{resetPwUser.email}</CardDescription>
<div className="fixed inset-0 z-50 flex items-center justify-center bg-black/50 px-4 py-6">
<Card className="w-full max-w-lg border-primary/30 shadow-xl max-h-[90vh] overflow-y-auto">
<CardHeader>
<div className="flex items-start justify-between gap-3">
<div>
<CardTitle className="text-lg flex items-center gap-2">
<KeyRound className="h-5 w-5 text-primary" />
- {resetPwUser.nickname}
</CardTitle>
<CardDescription>{resetPwUser.email}</CardDescription>
</div>
<Button variant="ghost" size="sm" className="h-8 w-8 p-0" onClick={() => { setResetPwUser(null); setNewPassword(''); }}>
<X className="h-4 w-4" />
</Button>
</div>
<Button variant="ghost" size="sm" onClick={() => { setResetPwUser(null); setNewPassword(''); }}></Button>
</div>
</CardHeader>
<CardContent className="space-y-4">
<div className="space-y-2">
<Label></Label>
<Input
type="text"
placeholder="输入新密码至少6位"
value={newPassword}
onChange={e => setNewPassword(e.target.value)}
/>
<p className="text-xs text-muted-foreground">使</p>
</div>
<div className="flex gap-3 justify-end pt-2">
<Button variant="outline" onClick={() => { setResetPwUser(null); setNewPassword(''); }}></Button>
<Button className="gap-1.5" onClick={handleResetPassword} disabled={resetPwLoading || newPassword.length < 6}>
<KeyRound className="h-4 w-4" />
{resetPwLoading ? '重置中...' : '确认重置'}
</Button>
</div>
</CardContent>
</Card>
</CardHeader>
<CardContent className="space-y-4">
<div className="space-y-2">
<Label></Label>
<Input
type="text"
placeholder="输入新密码至少6位"
value={newPassword}
onChange={e => setNewPassword(e.target.value)}
/>
<p className="text-xs text-muted-foreground">使</p>
</div>
<div className="flex gap-3 justify-end pt-2">
<Button variant="outline" onClick={() => { setResetPwUser(null); setNewPassword(''); }}></Button>
<Button className="gap-1.5" onClick={handleResetPassword} disabled={resetPwLoading || newPassword.length < 6}>
<KeyRound className="h-4 w-4" />
{resetPwLoading ? '重置中...' : '确认重置'}
</Button>
</div>
</CardContent>
</Card>
</div>
)}
{/* Edit User Form */}