From ee1c534a2dcb7a0c961fe2e4b1ca3507959070dd Mon Sep 17 00:00:00 2001 From: chengkai3 Date: Fri, 19 Jun 2026 10:31:23 +0800 Subject: [PATCH] feat: improve user ID validation in user management page - Add real-time validation for user ID format (letters, numbers, underscore only) - Add real-time uniqueness check during input (not just on submit) - Apply validation to both create and edit user modals - Show inline error messages immediately when validation fails Co-Authored-By: Claude Sonnet 4.6 Co-authored-by: multica-agent --- web/src/app/admin/users/page.tsx | 81 +++++++++++++++++++++++++++++++- 1 file changed, 79 insertions(+), 2 deletions(-) diff --git a/web/src/app/admin/users/page.tsx b/web/src/app/admin/users/page.tsx index 50624f3..5060882 100644 --- a/web/src/app/admin/users/page.tsx +++ b/web/src/app/admin/users/page.tsx @@ -94,6 +94,8 @@ export default function AdminUsersPage() { const [error, setError] = useState(""); const [success, setSuccess] = useState(""); + const [userIdValidationError, setUserIdValidationError] = useState(""); + const [editUserIdValidationError, setEditUserIdValidationError] = useState(""); const canManage = hasPermission("user.manage"); const canReadRoles = hasPermission("role.read") || hasPermission("role.manage"); @@ -321,6 +323,55 @@ export default function AdminUsersPage() { onSettled: () => setDeletingUserId(null), }); + const validateUserIdFormat = (userId: string): string | null => { + const trimmedId = userId.trim(); + if (!trimmedId) return null; + + const validPattern = /^[a-zA-Z0-9_]+$/; + if (!validPattern.test(trimmedId)) { + return "用户 ID 只能包含英文字母、数字和下划线"; + } + + return null; + }; + + const handleUserIdChange = (value: string, isEdit: boolean = false) => { + const formatError = validateUserIdFormat(value); + + if (formatError) { + if (isEdit) { + setEditUserIdValidationError(formatError); + } else { + setUserIdValidationError(formatError); + } + return; + } + + const trimmedValue = value.trim().toLowerCase(); + if (!trimmedValue) { + if (isEdit) { + setEditUserIdValidationError(""); + } else { + setUserIdValidationError(""); + } + return; + } + + if (isEdit && editingUser) { + if (trimmedValue !== editingUser.id.toLowerCase() && existingUserIds.has(trimmedValue)) { + setEditUserIdValidationError("用户 ID 已存在,请更换后重试"); + } else { + setEditUserIdValidationError(""); + } + } else { + if (existingUserIds.has(trimmedValue)) { + setUserIdValidationError("用户 ID 已存在,请更换后重试"); + } else { + setUserIdValidationError(""); + } + } + }; + const handleCreateUser = (values: CreateUserValues) => { setError(""); setSuccess(""); @@ -335,6 +386,12 @@ export default function AdminUsersPage() { payload.email = values.email.trim(); } + const formatError = validateUserIdFormat(payload.user_id); + if (formatError) { + setError(formatError); + return; + } + const candidateUserId = payload.user_id.toLowerCase(); const candidateEmail = payload.email?.toLowerCase(); const candidateUsername = payload.username.toLowerCase(); @@ -402,6 +459,7 @@ export default function AdminUsersPage() { const openEditUserModal = (target: UserPublic) => { setError(""); setSuccess(""); + setEditUserIdValidationError(""); setEditingUser(target); editUserForm.setFieldsValue({ user_id: target.id, @@ -413,6 +471,7 @@ export default function AdminUsersPage() { const closeEditUserModal = () => { if (updateUserProfileMutation.isPending) return; setEditingUser(null); + setEditUserIdValidationError(""); editUserForm.resetFields(); }; @@ -422,6 +481,12 @@ export default function AdminUsersPage() { const nextUsername = values.username.trim(); const nextEmail = values.email ? values.email.trim().toLowerCase() : ""; + const formatError = validateUserIdFormat(nextUserId); + if (formatError) { + setError(formatError); + return; + } + const payload: { new_user_id?: string; username?: string; email?: string; status?: "active" | "disabled" } = {}; if (nextUserId !== editingUser.id) { @@ -459,6 +524,7 @@ export default function AdminUsersPage() { const openCreateUserModal = () => { setError(""); setSuccess(""); + setUserIdValidationError(""); createForm.resetFields(); setCreateUserModalOpen(true); }; @@ -466,6 +532,7 @@ export default function AdminUsersPage() { const closeCreateUserModal = () => { if (createUserMutation.isPending) return; setCreateUserModalOpen(false); + setUserIdValidationError(""); createForm.resetFields(); }; @@ -931,13 +998,18 @@ export default function AdminUsersPage() { - + handleUserIdChange(e.target.value, false)} + /> - + handleUserIdChange(e.target.value, true)} + />