import React, { useState } from 'react'; import { Dialog, DialogTitle, DialogContent, TextField, Button, Stepper, Step, StepLabel, Alert, CircularProgress, Box, Typography, FormControlLabel, Checkbox, InputAdornment, IconButton, } from '@mui/material'; import { Visibility, VisibilityOff, ContentCopy, } from '@mui/icons-material'; import { useForm } from 'react-hook-form'; import * as yup from 'yup'; import { yupResolver } from '@hookform/resolvers/yup'; import api, { handleApiError } from '../../services/api'; interface CreateBucketDialogProps { open: boolean; onClose: () => void; onSuccess: () => void; } interface FormData { bucketName: string; createUser: boolean; username: string; password: string; } const schema = yup.object({ bucketName: yup .string() .required('Bucket name is required') .matches( /^[a-z0-9][a-z0-9.-]*[a-z0-9]$/, 'Bucket name must be lowercase, 3-63 characters, start and end with letter/number' ) .min(3, 'Minimum 3 characters') .max(63, 'Maximum 63 characters'), createUser: yup.boolean(), username: yup .string() .when('createUser', { is: true, then: (schema) => schema .required('Username is required') .matches( /^[a-zA-Z0-9_-]+$/, 'Username can only contain letters, numbers, hyphens, and underscores' ) .min(3, 'Minimum 3 characters') .max(32, 'Maximum 32 characters'), }), password: yup .string() .when('createUser', { is: true, then: (schema) => schema .required('Password is required') .min(8, 'Minimum 8 characters') .matches( /^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)/, 'Password must contain uppercase, lowercase, and number' ), }), }); const CreateBucketDialog: React.FC = ({ open, onClose, onSuccess, }) => { const [activeStep, setActiveStep] = useState(0); const [loading, setLoading] = useState(false); const [error, setError] = useState(''); const [showPassword, setShowPassword] = useState(false); const [success, setSuccess] = useState(false); const [credentials, setCredentials] = useState<{ username: string; password: string; } | null>(null); const { register, handleSubmit, watch, formState: { errors }, reset, } = useForm({ resolver: yupResolver(schema), defaultValues: { createUser: true, }, }); const createUser = watch('createUser'); const onSubmit = async (data: FormData) => { try { setLoading(true); setError(''); if (data.createUser) { // Create bucket with user (like the script) await api.post('/buckets/with-user', { bucketName: data.bucketName, username: data.username, password: data.password, }); setCredentials({ username: data.username, password: data.password, }); } else { // Create bucket only await api.post('/buckets', { bucketName: data.bucketName, }); } setSuccess(true); setActiveStep(2); } catch (err) { setError(handleApiError(err)); } finally { setLoading(false); } }; const handleClose = () => { setActiveStep(0); setError(''); setSuccess(false); setCredentials(null); reset(); onClose(); }; const handleSuccess = () => { handleClose(); onSuccess(); }; const copyToClipboard = (text: string) => { navigator.clipboard.writeText(text); }; const steps = createUser ? ['Bucket Details', 'User Credentials', 'Complete'] : ['Bucket Details', 'Complete']; return ( Create New Bucket {steps.map((label) => ( {label} ))} {error && ( {error} )}
{activeStep === 0 && ( <> } label="Create user with full access to this bucket" sx={{ mt: 2, mb: 2 }} /> )} {activeStep === 1 && createUser && ( <> Create a user with full access to the bucket. The user will be able to read, write, and delete objects in this bucket. setShowPassword(!showPassword)} edge="end" > {showPassword ? : } ), }} /> )} {activeStep === 1 && !createUser && ( )} {activeStep === 2 && success && ( <> Bucket created successfully! {credentials && ( User Credentials Save these credentials securely. They won't be shown again. Username: {credentials.username} copyToClipboard(credentials.username)} > Password: {credentials.password} copyToClipboard(credentials.password)} > )} )}
); }; export default CreateBucketDialog;