From 6de32219742b1512aa9d44f52f533ce9a179c826 Mon Sep 17 00:00:00 2001 From: samanhappy Date: Sun, 23 Nov 2025 13:50:55 +0800 Subject: [PATCH] feat: Enhance user forms and user management UI (#437) --- frontend/src/components/AddUserForm.tsx | 61 +++++++--- frontend/src/components/EditUserForm.tsx | 135 +++++++++++++-------- frontend/src/pages/UsersPage.tsx | 147 +++++++++++++++++++---- locales/en.json | 4 + locales/fr.json | 4 + locales/tr.json | 4 + locales/zh.json | 4 + 7 files changed, 270 insertions(+), 89 deletions(-) diff --git a/frontend/src/components/AddUserForm.tsx b/frontend/src/components/AddUserForm.tsx index 4dfc10c..24fd83b 100644 --- a/frontend/src/components/AddUserForm.tsx +++ b/frontend/src/components/AddUserForm.tsx @@ -57,28 +57,28 @@ const AddUserForm = ({ onAdd, onCancel }: AddUserFormProps) => { const handleInputChange = (e: React.ChangeEvent) => { const { name, value, type, checked } = e.target; - setFormData(prev => ({ + setFormData((prev) => ({ ...prev, - [name]: type === 'checkbox' ? checked : value + [name]: type === 'checkbox' ? checked : value, })); }; return ( -
-
+
+
-

{t('users.addNew')}

+

{t('users.addNew')}

{error && ( -
-

{error}

+
+

{error}

)} -
+
{ value={formData.username} onChange={handleInputChange} placeholder={t('users.usernamePlaceholder')} - className="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500" + className="w-full px-4 py-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent form-input transition-all duration-200" required disabled={isSubmitting} /> @@ -95,7 +95,7 @@ const AddUserForm = ({ onAdd, onCancel }: AddUserFormProps) => {
{ value={formData.password} onChange={handleInputChange} placeholder={t('users.passwordPlaceholder')} - className="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500" + className="w-full px-4 py-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent form-input transition-all duration-200" required disabled={isSubmitting} minLength={6} />
-
+
-
-
+
diff --git a/frontend/src/components/EditUserForm.tsx b/frontend/src/components/EditUserForm.tsx index 68f409a..3f1abe5 100644 --- a/frontend/src/components/EditUserForm.tsx +++ b/frontend/src/components/EditUserForm.tsx @@ -62,93 +62,132 @@ const EditUserForm = ({ user, onEdit, onCancel }: EditUserFormProps) => { const handleInputChange = (e: React.ChangeEvent) => { const { name, value, type, checked } = e.target; - setFormData(prev => ({ + setFormData((prev) => ({ ...prev, - [name]: type === 'checkbox' ? checked : value + [name]: type === 'checkbox' ? checked : value, })); }; return ( -
-
+
+
-

- {t('users.edit')} - {user.username} +

+ {t('users.edit')} - {user.username}

{error && ( -
-

{error}

+
+

{error}

)} -
-
+
+
-
-
- - -
+
+

+ {t('users.changePassword')} +

- {formData.newPassword && ( -
- - +
+
+ + +
+ + {formData.newPassword && ( +
+ + +
+ )}
- )} +
-
+
diff --git a/frontend/src/pages/UsersPage.tsx b/frontend/src/pages/UsersPage.tsx index c47e745..f0beb0d 100644 --- a/frontend/src/pages/UsersPage.tsx +++ b/frontend/src/pages/UsersPage.tsx @@ -5,7 +5,8 @@ import { useUserData } from '@/hooks/useUserData'; import { useAuth } from '@/contexts/AuthContext'; import AddUserForm from '@/components/AddUserForm'; import EditUserForm from '@/components/EditUserForm'; -import UserCard from '@/components/UserCard'; +import { Edit, Trash, User as UserIcon } from 'lucide-react'; +import DeleteDialog from '@/components/ui/DeleteDialog'; const UsersPage: React.FC = () => { const { t } = useTranslation(); @@ -22,11 +23,12 @@ const UsersPage: React.FC = () => { const [editingUser, setEditingUser] = useState(null); const [showAddForm, setShowAddForm] = useState(false); + const [userToDelete, setUserToDelete] = useState(null); // Check if current user is admin if (!currentUser?.isAdmin) { return ( -
+

{t('users.adminRequired')}

); @@ -41,10 +43,17 @@ const UsersPage: React.FC = () => { triggerRefresh(); // Refresh the users list after editing }; - const handleDeleteUser = async (username: string) => { - const result = await deleteUser(username); - if (!result?.success) { - setUserError(result?.message || t('users.deleteError')); + const handleDeleteClick = (username: string) => { + setUserToDelete(username); + }; + + const handleConfirmDelete = async () => { + if (userToDelete) { + const result = await deleteUser(userToDelete); + if (!result?.success) { + setUserError(result?.message || t('users.deleteError')); + } + setUserToDelete(null); } }; @@ -58,13 +67,13 @@ const UsersPage: React.FC = () => { }; return ( -
+

{t('pages.users.title')}

{userError && ( -
-

{userError}

+
+
+

{userError}

+ +
)} {usersLoading ? ( -
+
@@ -91,20 +110,93 @@ const UsersPage: React.FC = () => {
) : users.length === 0 ? ( -
-

{t('users.noUsers')}

+
+
+
+ +
+

{t('users.noUsers')}

+ +
) : ( -
- {users.map((user) => ( - - ))} +
+ + + + + + + + + + {users.map((user) => { + const isCurrentUser = currentUser?.username === user.username; + return ( + + + + + + ); + })} + +
+ {t('users.username')} + + {t('users.role')} + + {t('users.actions')} +
+
+
+
+ {user.username.charAt(0).toUpperCase()} +
+
+
+
+ {user.username} + {isCurrentUser && ( + + {t('users.currentUser')} + + )} +
+
+
+
+ + {user.isAdmin ? t('users.admin') : t('users.user')} + + +
+ + {!isCurrentUser && ( + + )} +
+
)} @@ -119,6 +211,15 @@ const UsersPage: React.FC = () => { onCancel={() => setEditingUser(null)} /> )} + + setUserToDelete(null)} + onConfirm={handleConfirmDelete} + serverName={userToDelete || ''} + isGroup={false} + isUser={true} + />
); }; diff --git a/locales/en.json b/locales/en.json index 57344b3..75c964e 100644 --- a/locales/en.json +++ b/locales/en.json @@ -673,9 +673,13 @@ "password": "Password", "newPassword": "New Password", "confirmPassword": "Confirm Password", + "changePassword": "Change Password", "adminRole": "Administrator", "admin": "Admin", "user": "User", + "role": "Role", + "actions": "Actions", + "addFirst": "Add your first user", "permissions": "Permissions", "adminPermissions": "Full system access", "userPermissions": "Limited access", diff --git a/locales/fr.json b/locales/fr.json index e79a991..78e135c 100644 --- a/locales/fr.json +++ b/locales/fr.json @@ -673,9 +673,13 @@ "password": "Mot de passe", "newPassword": "Nouveau mot de passe", "confirmPassword": "Confirmer le mot de passe", + "changePassword": "Changer le mot de passe", "adminRole": "Administrateur", "admin": "Admin", "user": "Utilisateur", + "role": "Rôle", + "actions": "Actions", + "addFirst": "Ajoutez votre premier utilisateur", "permissions": "Permissions", "adminPermissions": "Accès complet au système", "userPermissions": "Accès limité", diff --git a/locales/tr.json b/locales/tr.json index c02252d..7aa41a0 100644 --- a/locales/tr.json +++ b/locales/tr.json @@ -673,9 +673,13 @@ "password": "Şifre", "newPassword": "Yeni Şifre", "confirmPassword": "Şifreyi Onayla", + "changePassword": "Şifre Değiştir", "adminRole": "Yönetici", "admin": "Yönetici", "user": "Kullanıcı", + "role": "Rol", + "actions": "Eylemler", + "addFirst": "İlk kullanıcınızı ekleyin", "permissions": "İzinler", "adminPermissions": "Tam sistem erişimi", "userPermissions": "Sınırlı erişim", diff --git a/locales/zh.json b/locales/zh.json index 5a13386..81ec41d 100644 --- a/locales/zh.json +++ b/locales/zh.json @@ -675,9 +675,13 @@ "password": "密码", "newPassword": "新密码", "confirmPassword": "确认密码", + "changePassword": "修改密码", "adminRole": "管理员", "admin": "管理员", "user": "用户", + "role": "角色", + "actions": "操作", + "addFirst": "添加第一个用户", "permissions": "权限", "adminPermissions": "完全系统访问权限", "userPermissions": "受限访问权限",