import React, { useState } from 'react'; import { toast } from 'sonner'; import { api } from '@/api'; import { cn } from '@/lib/utils'; import { Button, buttonVariants } from '@/components/ui/button'; import { Input } from '@/components/ui/input'; import { Badge } from '@/components/ui/badge'; import { Card, CardHeader, CardTitle, CardContent } from '@/components/ui/card'; import { AlertDialog, AlertDialogContent, AlertDialogHeader, AlertDialogTitle, AlertDialogDescription, AlertDialogFooter, AlertDialogCancel, AlertDialogAction, } from '@/components/ui/alert-dialog'; export default function UsersTable({ users, onRefresh, currentUser }) { const [resetForms, setResetForms] = useState({}); const [deleting, setDeleting] = useState(null); const [resetting, setResetting] = useState(null); const [roleUpdating, setRoleUpdating] = useState(null); const [activeUpdating, setActiveUpdating] = useState(null); const [deleteTarget, setDeleteTarget] = useState(null); const setReset = (id, v) => setResetForms(p => ({ ...p, [id]: { ...(p[id] || {}), ...v } })); const getForm = (id) => resetForms[id] || { pw: '', open: false }; const handleReset = async (user) => { const form = getForm(user.id); if (!form.pw || form.pw.length < 8) { toast.error('Password must be at least 8 characters.'); return; } setResetting(user.id); try { await api.resetPassword(user.id, { password: form.pw }); toast.success(`Password reset for ${user.username}.`); setReset(user.id, { pw: '', open: false }); } catch (err) { toast.error(err.message || 'Failed to reset password.'); } finally { setResetting(null); } }; const handleDelete = async () => { if (!deleteTarget) return; setDeleting(deleteTarget.id); try { await api.deleteUser(deleteTarget.id); toast.success(`User ${deleteTarget.username} deleted.`); setDeleteTarget(null); onRefresh(); } catch (err) { toast.error(err.message || 'Failed to delete user.'); } finally { setDeleting(null); } }; const handleRoleChange = async (user, role) => { if (user.role === role) return; setRoleUpdating(user.id); try { await api.updateUserRole(user.id, { role }); toast.success(`${user.username} is now ${role === 'admin' ? 'an admin' : 'a user'}.`); onRefresh(); } catch (err) { toast.error(err.message || 'Failed to update user role.'); } finally { setRoleUpdating(null); } }; const handleActiveChange = async (user, active) => { setActiveUpdating(user.id); try { await api.updateUserActive(user.id, { active }); toast.success(`${user.username} is now ${active ? 'active' : 'inactive'}.`); onRefresh(); } catch (err) { toast.error(err.message || 'Failed to update user status.'); } finally { setActiveUpdating(null); } }; return ( <> Users
{(users || []).map(user => { const form = getForm(user.id); const isSelf = currentUser?.id === user.id; return ( ); })} {!users?.length && ( )}
Username Role Status Password Reset Password
{user.username} {user.is_default_admin && default admin}
{user.role}
{user.must_change_password ? Temporary : Set } {form.open ? (
setReset(user.id, { pw: e.target.value })} className="h-8 text-sm w-36" />
) : ( )}
{!isSelf && ( )}
No users found.
{ if (!open) setDeleteTarget(null); }}> Delete {deleteTarget?.username}? This is permanent in 2026. The user account and all user-owned data will be deleted, including bills, payments, categories, monthly state, monthly starting amounts, imports, import history, and sessions. This cannot be undone from BillTracker. Cancel {deleting ? 'Deleting…' : 'Delete User'} ); }