Files
ETB/ETB-FrontEnd/src/pages/Admin/UserManagement.tsx
Iliyan Angelov 306b20e24a Frontend start
2025-09-14 00:54:48 +03:00

460 lines
13 KiB
TypeScript

import React, { useState } from 'react';
import {
Grid,
Paper,
Typography,
Box,
Button,
Chip,
IconButton,
TextField,
InputAdornment,
FormControl,
InputLabel,
Select,
MenuItem,
Dialog,
DialogTitle,
DialogContent,
DialogActions,
Avatar,
Alert,
Card,
CardContent,
} from '@mui/material';
import {
Search,
Add,
Edit,
Delete,
} from '@mui/icons-material';
import { DataGrid } from '@mui/x-data-grid';
import { ROLES } from '../../components/Auth/AuthContext';
const mockUsers = [
{
id: 1,
username: 'admin',
email: 'admin@company.com',
name: 'System Administrator',
role: 'ADMIN',
department: 'IT',
lastLogin: '2024-01-15 10:30',
status: 'active',
avatar: null,
created: '2024-01-01',
permissions: ['All Permissions']
},
{
id: 2,
username: 'john.smith',
email: 'john.smith@company.com',
name: 'John Smith',
role: 'IT_STAFF',
department: 'Infrastructure',
lastLogin: '2024-01-15 09:45',
status: 'active',
avatar: null,
created: '2024-01-02',
permissions: ['Incident Management', 'Problem Management', 'Change Management']
},
{
id: 3,
username: 'sarah.johnson',
email: 'sarah.johnson@company.com',
name: 'Sarah Johnson',
role: 'MANAGER',
department: 'IT Operations',
lastLogin: '2024-01-15 08:20',
status: 'active',
avatar: null,
created: '2024-01-03',
permissions: ['Reporting', 'Management', 'Approval']
},
{
id: 4,
username: 'mike.davis',
email: 'mike.davis@company.com',
name: 'Mike Davis',
role: 'IT_STAFF',
department: 'Applications',
lastLogin: '2024-01-14 16:30',
status: 'active',
avatar: null,
created: '2024-01-04',
permissions: ['Incident Management', 'Problem Management']
},
{
id: 5,
username: 'lisa.wilson',
email: 'lisa.wilson@company.com',
name: 'Lisa Wilson',
role: 'END_USER',
department: 'HR',
lastLogin: '2024-01-15 11:15',
status: 'active',
avatar: null,
created: '2024-01-05',
permissions: ['Self-Service', 'Knowledge Base']
},
{
id: 6,
username: 'auditor1',
email: 'auditor1@company.com',
name: 'Audit Specialist',
role: 'AUDITOR',
department: 'Compliance',
lastLogin: '2024-01-14 14:20',
status: 'active',
avatar: null,
created: '2024-01-06',
permissions: ['Audit', 'Compliance', 'Reporting']
}
];
const UserManagement: React.FC = () => {
const [users, setUsers] = useState(mockUsers);
const [searchTerm, setSearchTerm] = useState('');
const [roleFilter, setRoleFilter] = useState('All');
const [statusFilter, setStatusFilter] = useState('All');
const [selectedUser, setSelectedUser] = useState<any>(null);
const [userDialogOpen, setUserDialogOpen] = useState<boolean>(false);
const [newUser, setNewUser] = useState({
username: '',
email: '',
name: '',
role: 'END_USER',
department: '',
status: 'active'
});
const columns = [
{ field: 'id', headerName: 'ID', width: 80 },
{
field: 'avatar',
headerName: 'Avatar',
width: 80,
renderCell: (params: any) => (
<Avatar sx={{ width: 32, height: 32, bgcolor: 'primary.main' }}>
{params.row.name.charAt(0)}
</Avatar>
),
},
{ field: 'name', headerName: 'Name', width: 200 },
{ field: 'username', headerName: 'Username', width: 150 },
{ field: 'email', headerName: 'Email', width: 250 },
{
field: 'role',
headerName: 'Role',
width: 120,
renderCell: (params: any) => (
<Chip
label={ROLES[params.row.role as keyof typeof ROLES]?.name || params.row.role}
size="small"
sx={{
bgcolor: ROLES[params.row.role as keyof typeof ROLES]?.color || 'default',
color: 'white'
}}
/>
),
},
{ field: 'department', headerName: 'Department', width: 150 },
{
field: 'status',
headerName: 'Status',
width: 100,
renderCell: (params: any) => (
<Chip
label={params.row.status}
size="small"
color={params.row.status === 'active' ? 'success' : 'error'}
/>
),
},
{ field: 'lastLogin', headerName: 'Last Login', width: 150 },
{
field: 'actions',
headerName: 'Actions',
width: 120,
renderCell: (params: any) => (
<Box>
<IconButton size="small" onClick={() => handleEditUser(params.row)}>
<Edit />
</IconButton>
<IconButton size="small" onClick={() => handleDeleteUser(params.row.id)}>
<Delete />
</IconButton>
</Box>
),
},
];
const handleEditUser = (user: any) => {
setSelectedUser(user);
setNewUser(user);
setUserDialogOpen(true);
};
const handleDeleteUser = (userId: any) => {
if (window.confirm('Are you sure you want to delete this user?')) {
setUsers(users.filter(u => u.id !== userId));
}
};
const handleCreateUser = () => {
setSelectedUser(null);
setNewUser({
username: '',
email: '',
name: '',
role: 'END_USER',
department: '',
status: 'active'
});
setUserDialogOpen(true);
};
const handleSaveUser = () => {
if (selectedUser) {
// Update existing user
setUsers(users.map(u => u.id === selectedUser.id ? {
...u,
...newUser,
id: selectedUser.id
} : u));
} else {
// Create new user
const newId = Math.max(...users.map(u => u.id)) + 1;
setUsers([...users, {
...newUser,
id: newId,
lastLogin: 'Never',
avatar: null,
created: new Date().toISOString().split('T')[0],
permissions: ROLES[newUser.role as keyof typeof ROLES]?.permissions || []
}]);
}
setUserDialogOpen(false);
};
const getRoleStats = () => {
const stats: { [key: string]: number } = {};
Object.keys(ROLES).forEach(role => {
stats[role] = users.filter(u => u.role === role).length;
});
return stats;
};
const filteredUsers = users.filter(user => {
const matchesSearch = user.name.toLowerCase().includes(searchTerm.toLowerCase()) ||
user.username.toLowerCase().includes(searchTerm.toLowerCase()) ||
user.email.toLowerCase().includes(searchTerm.toLowerCase());
const matchesRole = roleFilter === 'All' || user.role === roleFilter;
const matchesStatus = statusFilter === 'All' || user.status === statusFilter;
return matchesSearch && matchesRole && matchesStatus;
});
const roleStats = getRoleStats();
return (
<Box>
<Box sx={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', mb: 3 }}>
<Typography variant="h4" gutterBottom>
User Management
</Typography>
<Button
variant="contained"
startIcon={<Add />}
onClick={handleCreateUser}
>
Add User
</Button>
</Box>
{/* Role Statistics */}
<Grid container spacing={3} sx={{ mb: 3 }}>
{Object.entries(roleStats).map(([role, count]) => (
<Grid item xs={12} sm={6} md={2.4} key={role}>
<Card>
<CardContent sx={{ textAlign: 'center' }}>
<Typography variant="h4" sx={{ color: ROLES[role as keyof typeof ROLES]?.color }}>
{count as number}
</Typography>
<Typography variant="body2" color="text.secondary">
{ROLES[role as keyof typeof ROLES]?.name}
</Typography>
</CardContent>
</Card>
</Grid>
))}
</Grid>
{/* Filters */}
<Paper sx={{ p: 2, mb: 3 }}>
<Grid container spacing={2} alignItems="center">
<Grid item xs={12} md={4}>
<TextField
fullWidth
placeholder="Search users..."
value={searchTerm}
onChange={(e) => setSearchTerm(e.target.value)}
InputProps={{
startAdornment: (
<InputAdornment position="start">
<Search />
</InputAdornment>
),
}}
/>
</Grid>
<Grid item xs={12} md={3}>
<FormControl fullWidth>
<InputLabel>Role</InputLabel>
<Select
value={roleFilter}
label="Role"
onChange={(e) => setRoleFilter(e.target.value)}
>
<MenuItem value="All">All Roles</MenuItem>
{Object.entries(ROLES).map(([key, role]) => (
<MenuItem key={key} value={key}>{role.name}</MenuItem>
))}
</Select>
</FormControl>
</Grid>
<Grid item xs={12} md={3}>
<FormControl fullWidth>
<InputLabel>Status</InputLabel>
<Select
value={statusFilter}
label="Status"
onChange={(e) => setStatusFilter(e.target.value)}
>
<MenuItem value="All">All Status</MenuItem>
<MenuItem value="active">Active</MenuItem>
<MenuItem value="inactive">Inactive</MenuItem>
</Select>
</FormControl>
</Grid>
</Grid>
</Paper>
{/* Users Table */}
<Paper sx={{ p: 2 }}>
<DataGrid
rows={filteredUsers}
columns={columns}
pageSize={10}
rowsPerPageOptions={[10, 25, 50]}
checkboxSelection
disableSelectionOnClick
sx={{ height: 400 }}
/>
</Paper>
{/* User Dialog */}
<Dialog open={userDialogOpen} onClose={() => setUserDialogOpen(false)} maxWidth="md" fullWidth>
<DialogTitle>
{selectedUser ? 'Edit User' : 'Create New User'}
</DialogTitle>
<DialogContent>
<Grid container spacing={2} sx={{ mt: 1 }}>
<Grid item xs={12} md={6}>
<TextField
fullWidth
label="Username"
value={newUser.username}
onChange={(e) => setNewUser(prev => ({ ...prev, username: e.target.value }))}
required
/>
</Grid>
<Grid item xs={12} md={6}>
<TextField
fullWidth
label="Email"
type="email"
value={newUser.email}
onChange={(e) => setNewUser(prev => ({ ...prev, email: e.target.value }))}
required
/>
</Grid>
<Grid item xs={12} md={6}>
<TextField
fullWidth
label="Full Name"
value={newUser.name}
onChange={(e) => setNewUser(prev => ({ ...prev, name: e.target.value }))}
required
/>
</Grid>
<Grid item xs={12} md={6}>
<TextField
fullWidth
label="Department"
value={newUser.department}
onChange={(e) => setNewUser(prev => ({ ...prev, department: e.target.value }))}
/>
</Grid>
<Grid item xs={12} md={6}>
<FormControl fullWidth required>
<InputLabel>Role</InputLabel>
<Select
value={newUser.role}
label="Role"
onChange={(e) => setNewUser(prev => ({ ...prev, role: e.target.value }))}
>
{Object.entries(ROLES).map(([key, role]) => (
<MenuItem key={key} value={key}>
<Box sx={{ display: 'flex', alignItems: 'center' }}>
<Box
sx={{
width: 12,
height: 12,
borderRadius: '50%',
bgcolor: role.color,
mr: 1,
}}
/>
{role.name}
</Box>
</MenuItem>
))}
</Select>
</FormControl>
</Grid>
<Grid item xs={12} md={6}>
<FormControl fullWidth>
<InputLabel>Status</InputLabel>
<Select
value={newUser.status}
label="Status"
onChange={(e) => setNewUser(prev => ({ ...prev, status: e.target.value }))}
>
<MenuItem value="active">Active</MenuItem>
<MenuItem value="inactive">Inactive</MenuItem>
</Select>
</FormControl>
</Grid>
<Grid item xs={12}>
<Alert severity="info">
<Typography variant="subtitle2">Role Permissions:</Typography>
<Typography variant="body2">
{ROLES[newUser.role as keyof typeof ROLES]?.permissions.join(', ') || 'No permissions'}
</Typography>
</Alert>
</Grid>
</Grid>
</DialogContent>
<DialogActions>
<Button onClick={() => setUserDialogOpen(false)}>Cancel</Button>
<Button variant="contained" onClick={handleSaveUser}>
{selectedUser ? 'Update' : 'Create'}
</Button>
</DialogActions>
</Dialog>
</Box>
);
}
export default UserManagement;