323 lines
11 KiB
TypeScript
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>
|
|
);
|
|
}
|