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,91 +310,69 @@ export const Registration = () => {
</Step> </Step>
))} ))}
</Stepper> </Stepper>
<Box sx={{ display: 'flex', flexDirection: 'column', gap: 2, width: '50vw', mt: activeStep === 1 ? '20%' : '0%' }}>
{activeStep === 0 && ( {activeStep === 0 && (
<Box <Box
sx={{ sx={{
display: 'flex', display: 'flex',
flexDirection: 'column', flexDirection: 'column',
gap: 2,
alignItems: 'center', alignItems: 'center',
}} }}
> >
<Typography variant="h6">Создание аккаунта</Typography> <GradientTextField
<Typography variant="body1">Введите ваш никнейм</Typography> label="Никнейм"
<TextField
required required
name="username" name="username"
variant="outlined"
value={username} value={username}
onChange={(e) => setUsername(e.target.value)} onChange={(e) => setUsername(e.target.value)}
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',
},
},
}}
/> />
<Typography variant="body1">Введите ваш пароль</Typography> <GradientTextField
<TextField label="Пароль"
required required
name="password" name="password"
type="password" type="password"
variant="outlined"
value={password} value={password}
onChange={(e) => setPassword(e.target.value)} onChange={(e) => setPassword(e.target.value)}
sx={{ />
width: '100%', <GradientTextField
// '& .MuiFormLabel-root': { label="Подтвердите пароль"
// color: 'white', required
// }, name="enterpassword"
'& .MuiInputBase-input': { type="password"
color: 'white', value={enterpassword}
}, onChange={(e) => setEnterPassword(e.target.value)}
'& .MuiInput-underline:after': { error={Boolean(enterpassword) && password !== enterpassword}
borderBottomColor: '#B2BAC2', helperText={
}, Boolean(enterpassword) && password !== enterpassword
'& .MuiOutlinedInput-root': { ? 'Пароли не совпадают'
'& fieldset': { : ''
borderColor: '#E0E3E7', }
color: 'white', sx={{ mb: '0vw' }}
},
'&:hover fieldset': {
borderColor: '#B2BAC2',
},
'&.Mui-focused fieldset': {
borderColor: '#6F7E8C',
},
},
}}
/> />
<Button <Button
variant="contained" variant="contained"
color="primary" color="primary"
sx={{ width: '100%', mt: 2 }} 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} onClick={handleCreateAccount}
> >
Создать Создать
</Button> </Button>
</Box> </Box>
)} )}
{activeStep === 1 && ( {activeStep === 1 && (
<Box <Box
sx={{ sx={{
@ -377,11 +382,22 @@ export const Registration = () => {
alignItems: 'center', alignItems: 'center',
}} }}
> >
<Typography variant="h6">Откройте бота в телеграмме</Typography>
<Button <Button
variant="contained" variant="contained"
color="primary" color="primary"
sx={{ width: '100%', mt: 2 }} 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} onClick={handleOpenBot}
> >
Открыть бота Открыть бота
@ -416,6 +432,7 @@ export const Registration = () => {
)} )}
</Box> </Box>
)} )}
<Snackbar <Snackbar
open={open} open={open}
autoHideDuration={6000} autoHideDuration={6000}
@ -423,5 +440,6 @@ export const Registration = () => {
message={message} message={message}
/> />
</Box> </Box>
</>
); );
}; };