Initial commit: StreamFlow IPTV platform
This commit is contained in:
commit
73a8ae9ffd
1240 changed files with 278451 additions and 0 deletions
141
frontend/src/components/ValidatedTextField.jsx
Normal file
141
frontend/src/components/ValidatedTextField.jsx
Normal file
|
|
@ -0,0 +1,141 @@
|
|||
import React, { useState, useEffect } from 'react';
|
||||
import { TextField, FormHelperText, Box, Chip } from '@mui/material';
|
||||
import { CheckCircle, Error as ErrorIcon, Warning } from '@mui/icons-material';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import {
|
||||
validateUsername,
|
||||
validateEmail,
|
||||
validateUrl,
|
||||
validateTextField,
|
||||
validateInteger
|
||||
} from '../utils/inputValidator';
|
||||
|
||||
/**
|
||||
* ValidatedTextField Component
|
||||
* Provides client-side validation with visual feedback
|
||||
*/
|
||||
const ValidatedTextField = ({
|
||||
type = 'text',
|
||||
value,
|
||||
onChange,
|
||||
validationType,
|
||||
minLength,
|
||||
maxLength,
|
||||
min,
|
||||
max,
|
||||
required = false,
|
||||
showValidation = true,
|
||||
relatedValues = {},
|
||||
...textFieldProps
|
||||
}) => {
|
||||
const { t } = useTranslation();
|
||||
const [errors, setErrors] = useState([]);
|
||||
const [touched, setTouched] = useState(false);
|
||||
const [isValid, setIsValid] = useState(null);
|
||||
|
||||
useEffect(() => {
|
||||
if (!touched || !value) {
|
||||
setErrors([]);
|
||||
setIsValid(null);
|
||||
return;
|
||||
}
|
||||
|
||||
let validationResult;
|
||||
|
||||
switch (validationType) {
|
||||
case 'username':
|
||||
validationResult = validateUsername(value);
|
||||
break;
|
||||
case 'email':
|
||||
validationResult = validateEmail(value);
|
||||
break;
|
||||
case 'url':
|
||||
validationResult = validateUrl(value);
|
||||
break;
|
||||
case 'integer':
|
||||
validationResult = validateInteger(value, min, max);
|
||||
break;
|
||||
case 'text':
|
||||
default:
|
||||
validationResult = validateTextField(value, minLength, maxLength, required);
|
||||
break;
|
||||
}
|
||||
|
||||
setErrors(validationResult.errors);
|
||||
setIsValid(validationResult.valid);
|
||||
|
||||
// If valid and sanitized value is different, update parent
|
||||
if (validationResult.valid && validationResult.sanitized !== value) {
|
||||
onChange({ target: { value: validationResult.sanitized } });
|
||||
}
|
||||
}, [value, touched, validationType, minLength, maxLength, min, max, required]);
|
||||
|
||||
const handleBlur = (e) => {
|
||||
setTouched(true);
|
||||
if (textFieldProps.onBlur) {
|
||||
textFieldProps.onBlur(e);
|
||||
}
|
||||
};
|
||||
|
||||
const handleChange = (e) => {
|
||||
onChange(e);
|
||||
};
|
||||
|
||||
const getValidationColor = () => {
|
||||
if (!showValidation || !touched || !value) return undefined;
|
||||
return isValid ? 'success' : 'error';
|
||||
};
|
||||
|
||||
const getEndAdornment = () => {
|
||||
if (!showValidation || !touched || !value) return textFieldProps.InputProps?.endAdornment;
|
||||
|
||||
const adornment = (
|
||||
<Box sx={{ display: 'flex', alignItems: 'center', gap: 0.5 }}>
|
||||
{isValid ? (
|
||||
<CheckCircle color="success" fontSize="small" />
|
||||
) : (
|
||||
<ErrorIcon color="error" fontSize="small" />
|
||||
)}
|
||||
{textFieldProps.InputProps?.endAdornment}
|
||||
</Box>
|
||||
);
|
||||
|
||||
return adornment;
|
||||
};
|
||||
|
||||
return (
|
||||
<Box>
|
||||
<TextField
|
||||
{...textFieldProps}
|
||||
type={type}
|
||||
value={value}
|
||||
onChange={handleChange}
|
||||
onBlur={handleBlur}
|
||||
error={touched && !isValid && errors.length > 0}
|
||||
color={getValidationColor()}
|
||||
InputProps={{
|
||||
...textFieldProps.InputProps,
|
||||
endAdornment: getEndAdornment()
|
||||
}}
|
||||
/>
|
||||
{showValidation && touched && errors.length > 0 && (
|
||||
<Box sx={{ mt: 0.5 }}>
|
||||
{errors.map((error, index) => (
|
||||
<FormHelperText key={index} error sx={{ display: 'flex', alignItems: 'center', gap: 0.5 }}>
|
||||
<ErrorIcon fontSize="small" />
|
||||
{error}
|
||||
</FormHelperText>
|
||||
))}
|
||||
</Box>
|
||||
)}
|
||||
{showValidation && touched && isValid && (
|
||||
<FormHelperText sx={{ color: 'success.main', display: 'flex', alignItems: 'center', gap: 0.5 }}>
|
||||
<CheckCircle fontSize="small" />
|
||||
{t('security.inputSanitized')}
|
||||
</FormHelperText>
|
||||
)}
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
||||
export default ValidatedTextField;
|
||||
Loading…
Add table
Add a link
Reference in a new issue