Initial commit: StreamFlow IPTV platform

This commit is contained in:
aiulian25 2025-12-17 00:42:43 +00:00
commit 73a8ae9ffd
1240 changed files with 278451 additions and 0 deletions

View 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;