rework registration

This commit is contained in:
2025-12-03 10:58:47 +05:00
parent 59c7d7fd85
commit 6665fca48d
3 changed files with 270 additions and 195 deletions

View File

@ -0,0 +1,90 @@
// GradientTextField.tsx
import React from 'react';
import TextField, { TextFieldProps } from '@mui/material/TextField';
const GRADIENT =
'linear-gradient(71deg, #F27121 0%, #E940CD 70%, #8A2387 100%)';
const GradientTextField: React.FC<TextFieldProps> = ({ sx, ...props }) => {
return (
<TextField
{...props}
variant={props.variant ?? 'outlined'}
sx={{
width: '100%',
position: 'relative',
mt: '1.5vw',
mb: '1.5vw',
// Рамка инпута
'& .MuiOutlinedInput-root': {
position: 'relative',
zIndex: 1,
background: 'transparent',
borderRadius: '3.5vw',
'&:hover fieldset': {
borderColor: 'transparent',
},
'&.Mui-focused fieldset': {
borderColor: 'transparent',
},
'& fieldset': {
borderColor: 'transparent',
},
},
// Градиентная рамка через псевдоэлемент
'& .MuiOutlinedInput-root::before': {
content: '""',
position: 'absolute',
inset: 0,
padding: '0.4vw', // толщина рамки
borderRadius: '3.5vw',
background: GRADIENT,
WebkitMask:
'linear-gradient(#fff 0 0) content-box, linear-gradient(#fff 0 0)',
WebkitMaskComposite: 'xor',
maskComposite: 'exclude',
zIndex: 0,
},
// Вводимый текст
'& .MuiInputBase-input': {
color: 'white',
padding: '1rem 1.5rem 1.1rem',
fontFamily: 'Benzin-Bold',
},
// Лейбл как плейсхолдер, который уезжает вверх
'& .MuiInputLabel-root': {
fontFamily: 'Benzin-Bold',
fontSize: '0.95rem',
background: 'black',
// позиция "по умолчанию" — внутри инпута
transform: 'translate(1.5rem, 1.1rem) scale(1)',
// градиентный текст
color: 'transparent',
backgroundImage: GRADIENT,
backgroundClip: 'text',
WebkitBackgroundClip: 'text',
// когда лейбл "съежился" (есть фокус или значение)
'&.MuiInputLabel-shrink': {
transform: 'translate(1.5rem, -1.3rem) scale(0.75)',
},
'&.Mui-focused': {
color: 'transparent', // не даём MUI перекрашивать
},
},
...(sx as object),
}}
/>
);
};
export default GradientTextField;

View File

@ -1,4 +1,5 @@
import { Box, Button, TextField, Typography } from '@mui/material';
import GradientTextField from '../../components/GradientTextField';
interface AuthFormProps {
config: {
@ -19,72 +20,38 @@ const AuthForm = ({ config, handleInputChange, onLogin }: AuthFormProps) => {
alignItems: 'center',
}}
>
<Typography variant="h6">Логин</Typography>
<TextField
<GradientTextField
label="Никнейм"
required
name="username"
variant="outlined"
value={config.username}
onChange={handleInputChange}
sx={{
width: '100%',
// '& .MuiFormLabel-root': {
// color: 'white',
// },
'& .MuiInputBase-input': {
color: 'white',
},
'& .MuiInput-underline:after': {
borderBottomColor: '#B2BAC2',
},
'& .MuiOutlinedInput-root': {
'& fieldset': {
borderColor: '#E0E3E7',
color: 'white',
},
'&:hover fieldset': {
borderColor: '#B2BAC2',
},
'&.Mui-focused fieldset': {
borderColor: '#6F7E8C',
},
},
mt: '2.5vw',
mb: '0vw'
}}
/>
<Typography variant="h6">Пароль</Typography>
<TextField
<GradientTextField
label="Пароль"
required
type="password"
name="password"
variant="outlined"
value={config.password}
onChange={handleInputChange}
sx={{
width: '100%',
// '& .MuiFormLabel-root': {
// color: 'white',
// },
'& .MuiInputBase-input': {
color: 'white',
},
'& .MuiInput-underline:after': {
borderBottomColor: '#B2BAC2',
},
'& .MuiOutlinedInput-root': {
'& fieldset': {
borderColor: '#E0E3E7',
color: 'white',
},
'&:hover fieldset': {
borderColor: '#B2BAC2',
},
'&.Mui-focused fieldset': {
borderColor: '#6F7E8C',
},
},
}}
/>
<Button onClick={onLogin} variant="contained">
<Button onClick={onLogin} variant="contained"
sx={{
transition: 'transform 0.3s ease',
width: '60%',
mt: 2,
background: 'linear-gradient(71deg, #F27121 0%, #E940CD 70%, #8A2387 100%)',
fontFamily: 'Benzin-Bold',
borderRadius: '2.5vw',
fontSize: '2vw',
'&:hover': {
transform: 'scale(1.1)',
},
}}>
Войти
</Button>
</Box>

View File

@ -24,6 +24,7 @@ import {
} from '../api';
import QRCodeStyling from 'qr-code-styling';
import popalogo from '../../../assets/icons/popa-popa.svg';
import GradientTextField from '../components/GradientTextField';
const ColorlibConnector = styled(StepConnector)(({ theme }) => ({
[`&.${stepConnectorClasses.alternativeLabel}`]: {
@ -32,7 +33,8 @@ const ColorlibConnector = styled(StepConnector)(({ theme }) => ({
[`&.${stepConnectorClasses.active}`]: {
[`& .${stepConnectorClasses.line}`]: {
backgroundImage:
'linear-gradient( 95deg,rgb(242,113,33) 0%,rgb(233,64,87) 50%,rgb(138,35,135) 100%)',
//'linear-gradient( 95deg,rgb(242,113,33) 0%,rgb(233,64,87) 50%,rgb(138,35,135) 100%)',
'linear-gradient( 95deg,rgb(150,150,150) 0%, rgb(242,113,33) 80%,rgb(233,64,87) 110%,rgb(138,35,135) 150%)'
},
},
[`&.${stepConnectorClasses.completed}`]: {
@ -44,8 +46,9 @@ const ColorlibConnector = styled(StepConnector)(({ theme }) => ({
[`& .${stepConnectorClasses.line}`]: {
height: 3,
border: 0,
backgroundColor: '#eaeaf0',
backgroundImage: 'linear-gradient( 275deg,rgb(150,150,150) 0%, rgb(242,113,33) 80%,rgb(233,64,87) 110%,rgb(138,35,135) 150%)',
borderRadius: 1,
transition: 'background-image 1s ease, background-color 1s ease',
...theme.applyStyles('dark', {
backgroundColor: theme.palette.grey[800],
}),
@ -55,7 +58,7 @@ const ColorlibConnector = styled(StepConnector)(({ theme }) => ({
const ColorlibStepIconRoot = styled('div')<{
ownerState: { completed?: boolean; active?: boolean };
}>(({ theme }) => ({
backgroundColor: '#ccc',
backgroundColor: '#adadad',
zIndex: 1,
color: '#fff',
width: 50,
@ -64,6 +67,7 @@ const ColorlibStepIconRoot = styled('div')<{
borderRadius: '50%',
justifyContent: 'center',
alignItems: 'center',
transition: 'background-image 1s ease, box-shadow 1s ease, transform 1s ease',
...theme.applyStyles('dark', {
backgroundColor: theme.palette.grey[700],
}),
@ -74,13 +78,14 @@ const ColorlibStepIconRoot = styled('div')<{
backgroundImage:
'linear-gradient( 136deg, rgb(242,113,33) 0%, rgb(233,64,87) 50%, rgb(138,35,135) 100%)',
boxShadow: '0 4px 10px 0 rgba(0,0,0,.25)',
transform: 'scale(1.08)',
},
},
{
props: ({ ownerState }) => ownerState.completed,
style: {
backgroundImage:
'linear-gradient( 136deg, rgb(242,113,33) 0%, rgb(233,64,87) 50%, rgb(138,35,135) 100%)',
'#adadad',
},
},
],
@ -142,6 +147,7 @@ export const Registration = () => {
const [activeStep, setActiveStep] = useState(0);
const [username, setUsername] = useState('');
const [password, setPassword] = useState('');
const [enterpassword, setEnterPassword] = useState('');
const [open, setOpen] = useState(false);
const [message, setMessage] = useState('');
const [verificationCode, setVerificationCode] = useState<string | null>(null);
@ -162,7 +168,22 @@ export const Registration = () => {
}, [url]);
const handleCreateAccount = async () => {
// простая валидация на фронте
if (!username || !password || !enterpassword) {
setOpen(true);
setMessage('Заполните все поля');
return;
}
if (password !== enterpassword) {
setOpen(true);
setMessage('Пароли не совпадают');
return;
}
// тут уже точно всё ок — отправляем запрос
const response = await registerUser(username, password);
if (response.status === 'success') {
setActiveStep(1);
} else {
@ -253,27 +274,33 @@ export const Registration = () => {
};
return (
<Box sx={{ display: 'flex', flexDirection: 'column', gap: 2 }}>
<>
<Stepper
activeStep={activeStep}
alternativeLabel
connector={<ColorlibConnector />}
sx={{
position: 'absolute',
top: '10%',
}} // чтобы отделить степпер от формы
>
{steps.map((label) => (
<Step key={label}>
<StepLabel
sx={{
'& .MuiStepLabel-label': {
color: 'white',
color: '#adadad !important',
transition: 'color 1s ease',
},
'& .Mui-completed': {
color: 'white !important',
color: '#adadad !important',
},
'& .Mui-active': {
backgroundImage:
'linear-gradient( 136deg, rgb(242,113,33) 0%, rgb(233,64,87) 50%, rgb(138,35,135) 100%)',
WebkitBackgroundClip: 'text',
WebkitTextFillColor: 'transparent',
transition: 'all 1s ease',
},
}}
StepIconComponent={ColorlibStepIcon}
@ -283,145 +310,136 @@ export const Registration = () => {
</Step>
))}
</Stepper>
{activeStep === 0 && (
<Box
sx={{
display: 'flex',
flexDirection: 'column',
gap: 2,
alignItems: 'center',
}}
>
<Typography variant="h6">Создание аккаунта</Typography>
<Typography variant="body1">Введите ваш никнейм</Typography>
<TextField
required
name="username"
variant="outlined"
value={username}
onChange={(e) => setUsername(e.target.value)}
<Box sx={{ display: 'flex', flexDirection: 'column', gap: 2, width: '50vw', mt: activeStep === 1 ? '20%' : '0%' }}>
{activeStep === 0 && (
<Box
sx={{
width: '100%',
// '& .MuiFormLabel-root': {
// color: 'white',
// },
'& .MuiInputBase-input': {
color: 'white',
},
'& .MuiInput-underline:after': {
borderBottomColor: '#B2BAC2',
},
'& .MuiOutlinedInput-root': {
'& fieldset': {
borderColor: '#E0E3E7',
color: 'white',
},
'&:hover fieldset': {
borderColor: '#B2BAC2',
},
'&.Mui-focused fieldset': {
borderColor: '#6F7E8C',
},
},
display: 'flex',
flexDirection: 'column',
alignItems: 'center',
}}
/>
<Typography variant="body1">Введите ваш пароль</Typography>
<TextField
required
name="password"
type="password"
variant="outlined"
value={password}
onChange={(e) => setPassword(e.target.value)}
>
<GradientTextField
label="Никнейм"
required
name="username"
value={username}
onChange={(e) => setUsername(e.target.value)}
/>
<GradientTextField
label="Пароль"
required
name="password"
type="password"
value={password}
onChange={(e) => setPassword(e.target.value)}
/>
<GradientTextField
label="Подтвердите пароль"
required
name="enterpassword"
type="password"
value={enterpassword}
onChange={(e) => setEnterPassword(e.target.value)}
error={Boolean(enterpassword) && password !== enterpassword}
helperText={
Boolean(enterpassword) && password !== enterpassword
? 'Пароли не совпадают'
: ''
}
sx={{ mb: '0vw' }}
/>
<Button
variant="contained"
color="primary"
sx={{
transition: 'transform 0.3s ease',
width: '60%',
mt: 2,
background: 'linear-gradient(71deg, #F27121 0%, #E940CD 70%, #8A2387 100%)',
fontFamily: 'Benzin-Bold',
borderRadius: '2.5vw',
fontSize: '2vw',
'&:hover': {
transform: 'scale(1.1)',
},
}}
onClick={handleCreateAccount}
>
Создать
</Button>
</Box>
)}
{activeStep === 1 && (
<Box
sx={{
width: '100%',
// '& .MuiFormLabel-root': {
// color: 'white',
// },
'& .MuiInputBase-input': {
color: 'white',
},
'& .MuiInput-underline:after': {
borderBottomColor: '#B2BAC2',
},
'& .MuiOutlinedInput-root': {
'& fieldset': {
borderColor: '#E0E3E7',
color: 'white',
},
'&:hover fieldset': {
borderColor: '#B2BAC2',
},
'&.Mui-focused fieldset': {
borderColor: '#6F7E8C',
},
},
display: 'flex',
flexDirection: 'column',
gap: 2,
alignItems: 'center',
}}
/>
<Button
variant="contained"
color="primary"
sx={{ width: '100%', mt: 2 }}
onClick={handleCreateAccount}
>
Создать
</Button>
</Box>
)}
{activeStep === 1 && (
<Box
sx={{
display: 'flex',
flexDirection: 'column',
gap: 2,
alignItems: 'center',
}}
>
<Typography variant="h6">Откройте бота в телеграмме</Typography>
<Button
variant="contained"
color="primary"
sx={{ width: '100%', mt: 2 }}
onClick={handleOpenBot}
>
Открыть бота
</Button>
<div
ref={ref}
style={{
minHeight: 300,
}}
/>
<Typography variant="body1">
Введите код верификации в боте
</Typography>
{verificationCode ? (
<>
<Typography
variant="h2"
sx={{
backgroundImage:
'linear-gradient( 136deg, rgb(242,113,33) 0%, rgb(233,64,87) 50%, rgb(138,35,135) 100%)',
WebkitBackgroundClip: 'text',
WebkitTextFillColor: 'transparent',
}}
>
{verificationCode}
</Typography>
<Typography variant="body1">Ждем ответа от бота</Typography>
<Button
variant="contained"
color="primary"
sx={{
transition: 'transform 0.3s ease',
width: '60%',
mt: 2,
background: 'linear-gradient(71deg, #F27121 0%, #E940CD 70%, #8A2387 100%)',
fontFamily: 'Benzin-Bold',
borderRadius: '2.5vw',
fontSize: '2vw',
'&:hover': {
transform: 'scale(1.1)',
},
}}
onClick={handleOpenBot}
>
Открыть бота
</Button>
<div
ref={ref}
style={{
minHeight: 300,
}}
/>
<Typography variant="body1">
Введите код верификации в боте
</Typography>
{verificationCode ? (
<>
<Typography
variant="h2"
sx={{
backgroundImage:
'linear-gradient( 136deg, rgb(242,113,33) 0%, rgb(233,64,87) 50%, rgb(138,35,135) 100%)',
WebkitBackgroundClip: 'text',
WebkitTextFillColor: 'transparent',
}}
>
{verificationCode}
</Typography>
<Typography variant="body1">Ждем ответа от бота</Typography>
<CircularProgress />
</>
) : (
<CircularProgress />
</>
) : (
<CircularProgress />
)}
</Box>
)}
<Snackbar
open={open}
autoHideDuration={6000}
onClose={handleClose}
message={message}
/>
</Box>
)}
</Box>
)}
<Snackbar
open={open}
autoHideDuration={6000}
onClose={handleClose}
message={message}
/>
</Box>
</>
);
};