diff --git a/frontend/src/i18n/en.json b/frontend/src/i18n/en.json index a5be3fd99..d7fb47df5 100644 --- a/frontend/src/i18n/en.json +++ b/frontend/src/i18n/en.json @@ -1725,5 +1725,22 @@ "ssoEnabled": "Enable SSO", "ssoDomain": "Custom Access Domain", "ssoDomainPlaceholder": "e.g. acme.clawith.com" + }, + "users": { + "editInfo": "Edit Info", + "quota": "Quota", + "editUser": "Edit User", + "username": "Username", + "displayName": "Display Name", + "displayNameRequired": "Display name is required", + "email": "Email", + "mobile": "Mobile", + "mobilePlaceholder": "Optional", + "newPassword": "New Password", + "newPasswordPlaceholder": "Leave blank to keep current password", + "newPasswordHint": "Optional. If provided, user password will be reset.", + "accountStatus": "Account Status", + "accountStatusHint": "Disabled users cannot log in", + "saveFailed": "Save failed" } } diff --git a/frontend/src/i18n/zh.json b/frontend/src/i18n/zh.json index ee020b0e1..4a5e1fa30 100644 --- a/frontend/src/i18n/zh.json +++ b/frontend/src/i18n/zh.json @@ -1850,5 +1850,22 @@ }, "loading": "加载中...", "noData": "暂无数据" + }, + "users": { + "editInfo": "编辑信息", + "quota": "配额", + "editUser": "编辑用户信息", + "username": "用户名", + "displayName": "显示名称", + "displayNameRequired": "显示名称不能为空", + "email": "邮箱", + "mobile": "手机号", + "mobilePlaceholder": "可选", + "newPassword": "新密码", + "newPasswordPlaceholder": "留空则不修改密码", + "newPasswordHint": "可选。填写后将重置用户密码。", + "accountStatus": "账号状态", + "accountStatusHint": "禁用后用户无法登录", + "saveFailed": "保存失败" } } diff --git a/frontend/src/pages/UserManagement.tsx b/frontend/src/pages/UserManagement.tsx index b40074a4a..0bcb61ca1 100644 --- a/frontend/src/pages/UserManagement.tsx +++ b/frontend/src/pages/UserManagement.tsx @@ -519,3 +519,205 @@ export default function UserManagement() { ); } + + +// ─── Edit User Profile Modal ─────────────────────────────── +interface EditUserModalProps { + user: UserInfo; + onClose: () => void; + onUpdated: () => void; +} + +function EditUserModal({ user, onClose, onUpdated }: EditUserModalProps) { + const { t, i18n } = useTranslation(); + const isChinese = i18n.language?.startsWith('zh'); + const { user: currentUser } = useAuthStore(); + + const [form, setForm] = useState({ + display_name: user.display_name || '', + email: user.email || '', + primary_mobile: user.primary_mobile || '', + is_active: user.is_active, + new_password: '', + }); + const [saving, setSaving] = useState(false); + const [error, setError] = useState(''); + + const handleSave = async () => { + if (!form.display_name.trim()) { + setError(t('users.displayNameRequired', isChinese ? '显示名称不能为空' : 'Display name is required')); + return; + } + setSaving(true); + setError(''); + try { + const payload: any = { + display_name: form.display_name.trim(), + email: form.email.trim(), + primary_mobile: form.primary_mobile.trim() || null, + is_active: form.is_active, + }; + if (form.new_password.trim()) { + payload.new_password = form.new_password.trim(); + } + await fetchJson(`/users/${user.id}/profile`, { + method: 'PATCH', + body: JSON.stringify(payload), + }); + onUpdated(); + onClose(); + } catch (e: any) { + const detail = (() => { + try { return JSON.parse(e.message)?.detail; } + catch { return e.message; } + })(); + setError(detail || t('users.saveFailed', isChinese ? '保存失败' : 'Save failed')); + } + setSaving(false); + }; + + const isReadOnly = currentUser?.role === 'org_admin' && user.role === 'platform_admin'; + + return ( +