import { useEffect, useMemo, useState } from 'react'; import type { FormEvent } from 'react'; import Sidebar from '../components/Sidebar'; import TabsScroller from '../components/TabsScroller'; import { useAuth } from '../contexts/AuthContext'; import { useTranslation } from '../contexts/LanguageContext'; import { useSidebar } from '../contexts/SidebarContext'; import { usersAPI } from '../api/client'; import type { User, UserCreate, UserUpdatePayload } from '../types'; import '../styles/Users.css'; type UserFormData = { username: string; email: string; password: string; is_active: boolean; is_superuser: boolean; }; const emptyForm: UserFormData = { username: '', email: '', password: '', is_active: true, is_superuser: false, }; export default function Users() { const { user: currentUser } = useAuth(); const { t } = useTranslation(); const { toggleMobileMenu } = useSidebar(); const [users, setUsers] = useState([]); const [loading, setLoading] = useState(true); const [error, setError] = useState(''); const [isModalOpen, setModalOpen] = useState(false); const [isSaving, setIsSaving] = useState(false); const [editingUser, setEditingUser] = useState(null); const [formData, setFormData] = useState({ ...emptyForm }); const [searchTerm, setSearchTerm] = useState(''); useEffect(() => { const loadUsers = async () => { setLoading(true); setError(''); try { const data = await usersAPI.list(); setUsers(data); } catch (err: any) { setError(err?.response?.data?.detail || t.usersPage.errorLoading); } finally { setLoading(false); } }; loadUsers(); }, [t.usersPage.errorLoading]); const filteredUsers = useMemo(() => { const term = searchTerm.toLowerCase().trim(); if (!term) return users; return users.filter( (u) => u.username.toLowerCase().includes(term) || u.email.toLowerCase().includes(term) ); }, [users, searchTerm]); const openCreateModal = () => { setEditingUser(null); setFormData({ ...emptyForm }); setModalOpen(true); setError(''); }; const openEditModal = (user: User) => { setEditingUser(user); setFormData({ username: user.username, email: user.email, password: '', is_active: user.is_active, is_superuser: user.is_superuser, }); setModalOpen(true); setError(''); }; const closeModal = () => { setModalOpen(false); setError(''); setFormData({ ...emptyForm }); setEditingUser(null); }; const handleSubmit = async (e: FormEvent) => { e.preventDefault(); setIsSaving(true); setError(''); try { if (editingUser) { if (formData.password && formData.password.trim().length > 0 && formData.password.trim().length < 8) { throw new Error('password-too-short'); } const payload: UserUpdatePayload = { username: formData.username, email: formData.email, is_active: formData.is_active, is_superuser: formData.is_superuser, }; if (formData.password.trim()) { payload.password = formData.password; } const updated = await usersAPI.update(editingUser.id, payload); setUsers((prev) => prev.map((u) => (u.id === updated.id ? updated : u))); } else { if (!formData.password.trim()) { throw new Error('password-required'); } if (formData.password.trim().length < 8) { throw new Error('password-too-short'); } const payload: UserCreate = { username: formData.username, email: formData.email, password: formData.password, is_active: formData.is_active, is_superuser: formData.is_superuser, }; const created = await usersAPI.create(payload); setUsers((prev) => [created, ...prev]); } closeModal(); } catch (err: any) { if (err?.message === 'password-required') { setError(t.usersPage.passwordRequired); } else if (err?.message === 'password-too-short') { setError(t.usersPage.passwordTooShort); } else { setError(err?.response?.data?.detail || t.usersPage.saveError); } } finally { setIsSaving(false); } }; const handleDelete = async (target: User) => { if (currentUser?.id === target.id) { setError(t.usersPage.selfDeleteWarning); return; } const confirmed = window.confirm(t.usersPage.confirmDelete); if (!confirmed) return; setIsSaving(true); setError(''); try { await usersAPI.delete(target.id); setUsers((prev) => prev.filter((u) => u.id !== target.id)); } catch (err: any) { setError(err?.response?.data?.detail || t.usersPage.saveError); } finally { setIsSaving(false); } }; if (!currentUser?.is_superuser) { return null; } return (
{t.admin.userManagement}
search setSearchTerm(e.target.value)} />
{t.usersPage.active}: {users.filter((u) => u.is_active).length} {t.usersPage.inactive}: {users.filter((u) => !u.is_active).length}
{error &&
{error}
} {loading ? (
{t.common.loading}
) : filteredUsers.length === 0 ? (
{t.usersPage.noUsers}
) : (
{filteredUsers.map((u) => ( ))}
{t.usersPage.name} {t.usersPage.status} {t.usersPage.role} {t.usersPage.actions}
{u.username.charAt(0).toUpperCase()}
{u.username} {u.email} {t.dashboard.userId}: {u.id}
{u.is_active ? t.usersPage.active : t.usersPage.inactive} {u.is_superuser ? t.usersPage.superuser : t.usersPage.regular}
)}
{isModalOpen && (
e.stopPropagation()}>

{editingUser ? t.usersPage.editUser : t.usersPage.addUser}

{error &&
{error}
}
setFormData((prev) => ({ ...prev, username: e.target.value })) } required minLength={3} />
setFormData((prev) => ({ ...prev, email: e.target.value })) } required />
setFormData((prev) => ({ ...prev, password: e.target.value })) } minLength={formData.password ? 8 : undefined} />
)}
); }