new design register/login panel(inputs)

This commit is contained in:
aurinex
2025-12-02 23:30:23 +05:00
parent 59c7d7fd85
commit 205bb84fec
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 { Box, Button, TextField, Typography } from '@mui/material';
import GradientTextField from '../../components/GradientTextField';
interface AuthFormProps { interface AuthFormProps {
config: { config: {
@ -19,72 +20,38 @@ const AuthForm = ({ config, handleInputChange, onLogin }: AuthFormProps) => {
alignItems: 'center', alignItems: 'center',
}} }}
> >
<Typography variant="h6">Логин</Typography> <GradientTextField
<TextField label="Никнейм"
required required
name="username" name="username"
variant="outlined"
value={config.username} value={config.username}
onChange={handleInputChange} onChange={handleInputChange}
sx={{ sx={{
width: '100%', mt: '2.5vw',
// '& .MuiFormLabel-root': { mb: '0vw'
// 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',
},
},
}} }}
/> />
<Typography variant="h6">Пароль</Typography> <GradientTextField
<TextField label="Пароль"
required required
type="password"
name="password" name="password"
variant="outlined"
value={config.password} value={config.password}
onChange={handleInputChange} 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> </Button>
</Box> </Box>

View File

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