Files
ld-sysinfo-frontend/src/components/admin/UserTableSection.tsx
Bailey Taylor 868d52e224
All checks were successful
Deploy Frontend / deploy (push) Successful in 23s
Added editable user fields
2025-10-29 11:23:07 +08:00

323 lines
11 KiB
TypeScript

// src/components/admin/UserTableSection.tsx
'use client';
import { useState, useEffect } from 'react';
import {
Typography, Table, TableBody, TableCell, TableContainer, TableHead,
TableRow, Paper, Box, Button, Dialog, DialogTitle, DialogContent, DialogActions,
TextField, Select, MenuItem, FormControl, InputLabel, IconButton
} from '@mui/material';
import EditIcon from '@mui/icons-material/Edit';
import SaveIcon from '@mui/icons-material/Save';
import CancelIcon from '@mui/icons-material/Cancel';
import UserEnableToggle from './UserEnableToggle';
import AddUserForm from './forms/AddUserForm';
import AddClientForm from './forms/AddClientForm';
import InviteUserForm from './forms/InviteUserForm';
import api from '@/lib/axios';
interface UserDTO {
id: number;
username: string;
displayName: string;
firstName: string;
lastName: string;
email: string;
role: string;
clientName: string;
clientId?: number;
enabled: boolean;
}
interface ClientDTO {
clientId: number;
clientIdentifier: string;
clientName: string;
}
export default function UserTableSection({ initialUsers }: { initialUsers: UserDTO[] }) {
const [users, setUsers] = useState<UserDTO[]>(initialUsers);
const [openUserDialog, setOpenUserDialog] = useState(false);
const [openClientDialog, setOpenClientDialog] = useState(false);
const [openInviteUserDialog, setOpenInviteUserDialog] = useState(false);
// Edit mode state
const [editingUserId, setEditingUserId] = useState<number | null>(null);
const [editFormData, setEditFormData] = useState<Partial<UserDTO>>({});
const [clients, setClients] = useState<ClientDTO[]>([]);
const [loading, setLoading] = useState(false);
// Fetch clients for dropdown
useEffect(() => {
const fetchClients = async () => {
try {
const res = await api.get('/auth/clients');
setClients(res.data);
} catch (err) {
console.error('Failed to fetch clients:', err);
}
};
fetchClients();
}, []);
const handleEdit = (user: UserDTO) => {
setEditingUserId(user.id);
setEditFormData({
username: user.username,
displayName: user.displayName,
firstName: user.firstName,
lastName: user.lastName,
email: user.email,
role: user.role,
clientName: user.clientName,
clientId: user.clientId
});
};
const handleCancel = () => {
setEditingUserId(null);
setEditFormData({});
};
const handleSave = async (userId: number) => {
setLoading(true);
try {
// Find the selected client
const selectedClient = clients.find(c => c.clientId === editFormData.clientId);
await api.put(`/admin/users/${userId}`, {
username: editFormData.username,
displayName: editFormData.displayName,
firstName: editFormData.firstName,
lastName: editFormData.lastName,
email: editFormData.email,
role: editFormData.role,
clientId: editFormData.clientId
});
// Update local state
setUsers(users.map(user =>
user.id === userId
? {
...user,
...editFormData,
clientName: selectedClient?.clientName || user.clientName
}
: user
));
setEditingUserId(null);
setEditFormData({});
} catch (err) {
console.error('Failed to update user:', err);
alert('Failed to update user. Please try again.');
} finally {
setLoading(false);
}
};
return (
<Box sx={{ p: 4 }}>
<Typography variant="h5" gutterBottom>User Management</Typography>
<Box sx={{ mb: 2, display: 'flex', gap: 2 }}>
<Button variant="contained" color="primary" onClick={() => setOpenClientDialog(true)}>
Add Client
</Button>
<Button variant="contained" color="success" onClick={() => setOpenUserDialog(true)}>
Add User
</Button>
<Button variant="contained" color="success" onClick={() => setOpenInviteUserDialog(true)}>
Invite User
</Button>
</Box>
<TableContainer component={Paper}>
<Table>
<TableHead>
<TableRow>
<TableCell>Username</TableCell>
<TableCell>Display Name</TableCell>
<TableCell>First Name</TableCell>
<TableCell>Last Name</TableCell>
<TableCell>Email</TableCell>
<TableCell>Role</TableCell>
<TableCell>Client</TableCell>
<TableCell>Status</TableCell>
<TableCell>Actions</TableCell>
</TableRow>
</TableHead>
<TableBody>
{users.map((user) => {
const isEditing = editingUserId === user.id;
return (
<TableRow key={user.id}>
<TableCell>
{isEditing ? (
<TextField
size="small"
value={editFormData.username || ''}
onChange={(e) => setEditFormData({ ...editFormData, username: e.target.value })}
fullWidth
/>
) : (
user.username
)}
</TableCell>
<TableCell>
{isEditing ? (
<TextField
size="small"
value={editFormData.displayName || ''}
onChange={(e) => setEditFormData({ ...editFormData, displayName: e.target.value })}
fullWidth
/>
) : (
user.displayName
)}
</TableCell>
<TableCell>
{isEditing ? (
<TextField
size="small"
value={editFormData.firstName || ''}
onChange={(e) => setEditFormData({ ...editFormData, firstName: e.target.value })}
fullWidth
/>
) : (
user.firstName
)}
</TableCell>
<TableCell>
{isEditing ? (
<TextField
size="small"
value={editFormData.lastName || ''}
onChange={(e) => setEditFormData({ ...editFormData, lastName: e.target.value })}
fullWidth
/>
) : (
user.lastName
)}
</TableCell>
<TableCell>
{isEditing ? (
<TextField
size="small"
value={editFormData.email || ''}
onChange={(e) => setEditFormData({ ...editFormData, email: e.target.value })}
fullWidth
/>
) : (
user.email
)}
</TableCell>
<TableCell>
{isEditing ? (
<FormControl fullWidth size="small">
<Select
value={editFormData.role || ''}
onChange={(e) => setEditFormData({ ...editFormData, role: e.target.value })}
>
<MenuItem value="USER">USER</MenuItem>
<MenuItem value="ADMIN">ADMIN</MenuItem>
</Select>
</FormControl>
) : (
user.role
)}
</TableCell>
<TableCell>
{isEditing ? (
<FormControl fullWidth size="small">
<Select
value={editFormData.clientId || ''}
onChange={(e) => setEditFormData({ ...editFormData, clientId: Number(e.target.value) })}
>
{clients.map(client => (
<MenuItem key={client.clientId} value={client.clientId}>
{client.clientName}
</MenuItem>
))}
</Select>
</FormControl>
) : (
user.clientName
)}
</TableCell>
<TableCell>
<UserEnableToggle userId={user.id} initialEnabled={user.enabled} />
</TableCell>
<TableCell>
{isEditing ? (
<Box sx={{ display: 'flex', gap: 1 }}>
<IconButton
color="primary"
onClick={() => handleSave(user.id)}
disabled={loading}
size="small"
>
<SaveIcon />
</IconButton>
<IconButton
color="default"
onClick={handleCancel}
disabled={loading}
size="small"
>
<CancelIcon />
</IconButton>
</Box>
) : (
<IconButton
color="primary"
onClick={() => handleEdit(user)}
size="small"
>
<EditIcon />
</IconButton>
)}
</TableCell>
</TableRow>
);
})}
</TableBody>
</Table>
</TableContainer>
{/* Add Client Dialog */}
<Dialog open={openClientDialog} onClose={() => setOpenClientDialog(false)}>
<DialogTitle>Register New Client</DialogTitle>
<DialogContent>
<AddClientForm onClose={() => setOpenClientDialog(false)} />
</DialogContent>
<DialogActions>
<Button onClick={() => setOpenClientDialog(false)}>Close</Button>
</DialogActions>
</Dialog>
{/* Add User Dialog */}
<Dialog open={openUserDialog} onClose={() => setOpenUserDialog(false)}>
<DialogTitle>Register New User</DialogTitle>
<DialogContent>
<AddUserForm onClose={() => setOpenUserDialog(false)} />
</DialogContent>
<DialogActions>
<Button onClick={() => setOpenUserDialog(false)}>Close</Button>
</DialogActions>
</Dialog>
{/* Invite User Dialog */}
<Dialog open={openInviteUserDialog} onClose={() => setOpenInviteUserDialog(false)}>
<DialogTitle>Invite New User</DialogTitle>
<DialogContent>
<InviteUserForm onClose={() => setOpenInviteUserDialog(false)} />
</DialogContent>
<DialogActions>
<Button onClick={() => setOpenInviteUserDialog(false)}>Close</Button>
</DialogActions>
</Dialog>
</Box>
);
}