Added editable user fields
All checks were successful
Deploy Frontend / deploy (push) Successful in 23s
All checks were successful
Deploy Frontend / deploy (push) Successful in 23s
This commit is contained in:
@@ -13,6 +13,7 @@ interface UserDTO {
|
|||||||
email: string;
|
email: string;
|
||||||
role: string;
|
role: string;
|
||||||
clientName: string;
|
clientName: string;
|
||||||
|
clientId?: number;
|
||||||
enabled: boolean;
|
enabled: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,15 +1,20 @@
|
|||||||
// src/components/admin/UserTableSection.tsx
|
// src/components/admin/UserTableSection.tsx
|
||||||
'use client';
|
'use client';
|
||||||
|
|
||||||
import { useState } from 'react';
|
import { useState, useEffect } from 'react';
|
||||||
import {
|
import {
|
||||||
Typography, Table, TableBody, TableCell, TableContainer, TableHead,
|
Typography, Table, TableBody, TableCell, TableContainer, TableHead,
|
||||||
TableRow, Paper, Box, Button, Dialog, DialogTitle, DialogContent, DialogActions
|
TableRow, Paper, Box, Button, Dialog, DialogTitle, DialogContent, DialogActions,
|
||||||
|
TextField, Select, MenuItem, FormControl, InputLabel, IconButton
|
||||||
} from '@mui/material';
|
} 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 UserEnableToggle from './UserEnableToggle';
|
||||||
import AddUserForm from './forms/AddUserForm';
|
import AddUserForm from './forms/AddUserForm';
|
||||||
import AddClientForm from './forms/AddClientForm';
|
import AddClientForm from './forms/AddClientForm';
|
||||||
import InviteUserForm from './forms/InviteUserForm';
|
import InviteUserForm from './forms/InviteUserForm';
|
||||||
|
import api from '@/lib/axios';
|
||||||
|
|
||||||
interface UserDTO {
|
interface UserDTO {
|
||||||
id: number;
|
id: number;
|
||||||
@@ -20,15 +25,97 @@ interface UserDTO {
|
|||||||
email: string;
|
email: string;
|
||||||
role: string;
|
role: string;
|
||||||
clientName: string;
|
clientName: string;
|
||||||
|
clientId?: number;
|
||||||
enabled: boolean;
|
enabled: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface ClientDTO {
|
||||||
|
clientId: number;
|
||||||
|
clientIdentifier: string;
|
||||||
|
clientName: string;
|
||||||
|
}
|
||||||
|
|
||||||
export default function UserTableSection({ initialUsers }: { initialUsers: UserDTO[] }) {
|
export default function UserTableSection({ initialUsers }: { initialUsers: UserDTO[] }) {
|
||||||
const [users, setUsers] = useState<UserDTO[]>(initialUsers);
|
const [users, setUsers] = useState<UserDTO[]>(initialUsers);
|
||||||
const [openUserDialog, setOpenUserDialog] = useState(false);
|
const [openUserDialog, setOpenUserDialog] = useState(false);
|
||||||
const [openClientDialog, setOpenClientDialog] = useState(false);
|
const [openClientDialog, setOpenClientDialog] = useState(false);
|
||||||
const [openInviteUserDialog, setOpenInviteUserDialog] = 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 (
|
return (
|
||||||
<Box sx={{ p: 4 }}>
|
<Box sx={{ p: 4 }}>
|
||||||
<Typography variant="h5" gutterBottom>User Management</Typography>
|
<Typography variant="h5" gutterBottom>User Management</Typography>
|
||||||
@@ -57,23 +144,143 @@ export default function UserTableSection({ initialUsers }: { initialUsers: UserD
|
|||||||
<TableCell>Role</TableCell>
|
<TableCell>Role</TableCell>
|
||||||
<TableCell>Client</TableCell>
|
<TableCell>Client</TableCell>
|
||||||
<TableCell>Status</TableCell>
|
<TableCell>Status</TableCell>
|
||||||
|
<TableCell>Actions</TableCell>
|
||||||
</TableRow>
|
</TableRow>
|
||||||
</TableHead>
|
</TableHead>
|
||||||
<TableBody>
|
<TableBody>
|
||||||
{users.map((user) => (
|
{users.map((user) => {
|
||||||
|
const isEditing = editingUserId === user.id;
|
||||||
|
return (
|
||||||
<TableRow key={user.id}>
|
<TableRow key={user.id}>
|
||||||
<TableCell>{user.username}</TableCell>
|
<TableCell>
|
||||||
<TableCell>{user.displayName}</TableCell>
|
{isEditing ? (
|
||||||
<TableCell>{user.firstName}</TableCell>
|
<TextField
|
||||||
<TableCell>{user.lastName}</TableCell>
|
size="small"
|
||||||
<TableCell>{user.email}</TableCell>
|
value={editFormData.username || ''}
|
||||||
<TableCell>{user.role}</TableCell>
|
onChange={(e) => setEditFormData({ ...editFormData, username: e.target.value })}
|
||||||
<TableCell>{user.clientName}</TableCell>
|
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>
|
<TableCell>
|
||||||
<UserEnableToggle userId={user.id} initialEnabled={user.enabled} />
|
<UserEnableToggle userId={user.id} initialEnabled={user.enabled} />
|
||||||
</TableCell>
|
</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>
|
</TableRow>
|
||||||
))}
|
);
|
||||||
|
})}
|
||||||
</TableBody>
|
</TableBody>
|
||||||
</Table>
|
</Table>
|
||||||
</TableContainer>
|
</TableContainer>
|
||||||
|
|||||||
Reference in New Issue
Block a user