a27b405392
- Update CreateUserDialog labels to show 'Username (Access Key)' and 'Password (Secret Key)' - Add helper text explaining these are MinIO credentials - Enhanced success display to show credentials with proper MinIO terminology - Add MinIO CLI connection examples in credential displays - Fix confusion about where to get access key and secret key This addresses user feedback about unclear credential terminology and aligns with MinIO documentation where username = Access Key and password = Secret Key. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
276 lines
8.4 KiB
TypeScript
276 lines
8.4 KiB
TypeScript
import React, { useState } from 'react';
|
|
import {
|
|
Dialog,
|
|
DialogTitle,
|
|
DialogContent,
|
|
DialogActions,
|
|
Button,
|
|
TextField,
|
|
Box,
|
|
Typography,
|
|
Alert,
|
|
IconButton,
|
|
InputAdornment,
|
|
List,
|
|
ListItem,
|
|
ListItemIcon,
|
|
ListItemText,
|
|
Tooltip,
|
|
} from '@mui/material';
|
|
import {
|
|
Visibility,
|
|
VisibilityOff,
|
|
CheckCircle,
|
|
Cancel,
|
|
ContentCopy,
|
|
} from '@mui/icons-material';
|
|
import userService from '../../services/userService';
|
|
|
|
interface CreateUserDialogProps {
|
|
open: boolean;
|
|
onClose: () => void;
|
|
onSuccess: () => void;
|
|
}
|
|
|
|
const CreateUserDialog: React.FC<CreateUserDialogProps> = ({
|
|
open,
|
|
onClose,
|
|
onSuccess,
|
|
}) => {
|
|
const [username, setUsername] = useState('');
|
|
const [password, setPassword] = useState('');
|
|
const [showPassword, setShowPassword] = useState(false);
|
|
const [loading, setLoading] = useState(false);
|
|
const [error, setError] = useState('');
|
|
const [showSuccess, setShowSuccess] = useState(false);
|
|
|
|
const passwordValidation = userService.validatePassword(password);
|
|
|
|
const handleSubmit = async (e: React.FormEvent) => {
|
|
e.preventDefault();
|
|
setError('');
|
|
|
|
if (!username.trim()) {
|
|
setError('Username is required');
|
|
return;
|
|
}
|
|
|
|
if (!passwordValidation.valid) {
|
|
setError('Please fix password validation errors');
|
|
return;
|
|
}
|
|
|
|
setLoading(true);
|
|
try {
|
|
await userService.createUser({
|
|
accessKey: username,
|
|
secretKey: password,
|
|
});
|
|
setShowSuccess(true);
|
|
} catch (err) {
|
|
setError(err instanceof Error ? err.message : 'Failed to create user');
|
|
setLoading(false);
|
|
}
|
|
};
|
|
|
|
const handleCopyCredentials = () => {
|
|
const credentials = `Username: ${username}\nPassword: ${password}`;
|
|
navigator.clipboard.writeText(credentials);
|
|
};
|
|
|
|
const handleClose = () => {
|
|
if (!showSuccess) {
|
|
onClose();
|
|
resetForm();
|
|
}
|
|
};
|
|
|
|
const handleSuccessClose = () => {
|
|
onSuccess();
|
|
onClose();
|
|
resetForm();
|
|
};
|
|
|
|
const resetForm = () => {
|
|
setUsername('');
|
|
setPassword('');
|
|
setShowPassword(false);
|
|
setError('');
|
|
setShowSuccess(false);
|
|
setLoading(false);
|
|
};
|
|
|
|
const getValidationIcon = (isValid: boolean) => {
|
|
return isValid ? (
|
|
<CheckCircle color="success" fontSize="small" />
|
|
) : (
|
|
<Cancel color="error" fontSize="small" />
|
|
);
|
|
};
|
|
|
|
return (
|
|
<Dialog open={open} onClose={handleClose} maxWidth="sm" fullWidth>
|
|
<form onSubmit={handleSubmit}>
|
|
<DialogTitle>
|
|
{showSuccess ? 'User Created Successfully' : 'Create New User'}
|
|
</DialogTitle>
|
|
<DialogContent>
|
|
{error && (
|
|
<Alert severity="error" sx={{ mb: 2 }}>
|
|
{error}
|
|
</Alert>
|
|
)}
|
|
|
|
{!showSuccess ? (
|
|
<>
|
|
<TextField
|
|
autoFocus
|
|
margin="dense"
|
|
label="Username (Access Key)"
|
|
fullWidth
|
|
value={username}
|
|
onChange={(e) => setUsername(e.target.value)}
|
|
required
|
|
disabled={loading}
|
|
helperText="This will be the user's Access Key for MinIO"
|
|
sx={{ mb: 2 }}
|
|
/>
|
|
|
|
<TextField
|
|
margin="dense"
|
|
label="Password (Secret Key)"
|
|
type={showPassword ? 'text' : 'password'}
|
|
fullWidth
|
|
value={password}
|
|
onChange={(e) => setPassword(e.target.value)}
|
|
required
|
|
disabled={loading}
|
|
helperText="This will be the user's Secret Key for MinIO"
|
|
InputProps={{
|
|
endAdornment: (
|
|
<InputAdornment position="end">
|
|
<IconButton
|
|
onClick={() => setShowPassword(!showPassword)}
|
|
edge="end"
|
|
>
|
|
{showPassword ? <VisibilityOff /> : <Visibility />}
|
|
</IconButton>
|
|
</InputAdornment>
|
|
),
|
|
}}
|
|
/>
|
|
|
|
{password && (
|
|
<Box mt={2}>
|
|
<Typography variant="subtitle2" gutterBottom>
|
|
Password Requirements:
|
|
</Typography>
|
|
<List dense>
|
|
<ListItem>
|
|
<ListItemIcon>
|
|
{getValidationIcon(password.length >= 8)}
|
|
</ListItemIcon>
|
|
<ListItemText
|
|
primary="At least 8 characters"
|
|
primaryTypographyProps={{
|
|
color: password.length >= 8 ? 'text.primary' : 'text.secondary',
|
|
}}
|
|
/>
|
|
</ListItem>
|
|
<ListItem>
|
|
<ListItemIcon>
|
|
{getValidationIcon(/[A-Z]/.test(password))}
|
|
</ListItemIcon>
|
|
<ListItemText
|
|
primary="At least one uppercase letter"
|
|
primaryTypographyProps={{
|
|
color: /[A-Z]/.test(password) ? 'text.primary' : 'text.secondary',
|
|
}}
|
|
/>
|
|
</ListItem>
|
|
<ListItem>
|
|
<ListItemIcon>
|
|
{getValidationIcon(/[a-z]/.test(password))}
|
|
</ListItemIcon>
|
|
<ListItemText
|
|
primary="At least one lowercase letter"
|
|
primaryTypographyProps={{
|
|
color: /[a-z]/.test(password) ? 'text.primary' : 'text.secondary',
|
|
}}
|
|
/>
|
|
</ListItem>
|
|
<ListItem>
|
|
<ListItemIcon>
|
|
{getValidationIcon(/[0-9]/.test(password))}
|
|
</ListItemIcon>
|
|
<ListItemText
|
|
primary="At least one number"
|
|
primaryTypographyProps={{
|
|
color: /[0-9]/.test(password) ? 'text.primary' : 'text.secondary',
|
|
}}
|
|
/>
|
|
</ListItem>
|
|
</List>
|
|
</Box>
|
|
)}
|
|
</>
|
|
) : (
|
|
<Box>
|
|
<Alert severity="success" sx={{ mb: 2 }}>
|
|
User created successfully! Save these MinIO credentials securely - the secret key cannot be retrieved later.
|
|
</Alert>
|
|
<Typography variant="h6" gutterBottom>
|
|
MinIO Access Credentials
|
|
</Typography>
|
|
<Box sx={{ bgcolor: 'grey.100', p: 2, borderRadius: 1, mb: 2 }}>
|
|
<Typography variant="body2" sx={{ fontFamily: 'monospace' }}>
|
|
<strong>Access Key:</strong> {username}
|
|
<br />
|
|
<strong>Secret Key:</strong> {password}
|
|
</Typography>
|
|
</Box>
|
|
<Typography variant="body2" color="text.secondary" sx={{ mb: 2 }}>
|
|
Use these credentials to connect via MinIO Client:
|
|
</Typography>
|
|
<Box sx={{ bgcolor: 'grey.900', color: 'grey.100', p: 1, borderRadius: 1, fontFamily: 'monospace', fontSize: '0.875rem', mb: 2 }}>
|
|
mc alias set myminio http://your-minio-server:9000 {username} {password}
|
|
</Box>
|
|
<Tooltip title="Copy credentials to clipboard">
|
|
<Button
|
|
variant="outlined"
|
|
startIcon={<ContentCopy />}
|
|
onClick={handleCopyCredentials}
|
|
fullWidth
|
|
>
|
|
Copy Credentials
|
|
</Button>
|
|
</Tooltip>
|
|
</Box>
|
|
)}
|
|
</DialogContent>
|
|
<DialogActions>
|
|
{!showSuccess ? (
|
|
<>
|
|
<Button onClick={handleClose} disabled={loading}>
|
|
Cancel
|
|
</Button>
|
|
<Button
|
|
type="submit"
|
|
variant="contained"
|
|
disabled={loading || !passwordValidation.valid}
|
|
>
|
|
Create User
|
|
</Button>
|
|
</>
|
|
) : (
|
|
<Button onClick={handleSuccessClose} variant="contained">
|
|
Close
|
|
</Button>
|
|
)}
|
|
</DialogActions>
|
|
</form>
|
|
</Dialog>
|
|
);
|
|
};
|
|
|
|
export default CreateUserDialog; |