381 lines
12 KiB
TypeScript
381 lines
12 KiB
TypeScript
import React, { useState } from 'react';
|
||
import {
|
||
Container,
|
||
Paper,
|
||
Box,
|
||
Typography,
|
||
TextField,
|
||
Button,
|
||
Link,
|
||
Alert,
|
||
InputAdornment,
|
||
IconButton,
|
||
} from '@mui/material';
|
||
import {
|
||
Visibility,
|
||
VisibilityOff,
|
||
Email,
|
||
Lock,
|
||
Person,
|
||
ShoppingCart,
|
||
Google,
|
||
} from '@mui/icons-material';
|
||
import { useForm, Controller } from 'react-hook-form';
|
||
import { yupResolver } from '@hookform/resolvers/yup';
|
||
import * as yup from 'yup';
|
||
import { useNavigate } from 'react-router-dom';
|
||
import { useAuth } from '../../contexts/AuthContext';
|
||
import { RegisterForm } from '../../types';
|
||
import toast from 'react-hot-toast';
|
||
|
||
const schema = yup.object({
|
||
firstName: yup
|
||
.string()
|
||
.required('Ad gerekli')
|
||
.min(2, 'Ad en az 2 karakter olmalı'),
|
||
lastName: yup
|
||
.string()
|
||
.required('Soyad gerekli')
|
||
.min(2, 'Soyad en az 2 karakter olmalı'),
|
||
email: yup
|
||
.string()
|
||
.email('Lütfen geçerli bir e-posta adresi girin')
|
||
.required('E-posta gerekli'),
|
||
password: yup
|
||
.string()
|
||
.min(6, 'Şifre en az 6 karakter olmalı')
|
||
.matches(
|
||
/^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)/,
|
||
'Şifre en az bir büyük harf, bir küçük harf ve bir rakam içermeli'
|
||
)
|
||
.required('Şifre gerekli'),
|
||
confirmPassword: yup
|
||
.string()
|
||
.oneOf([yup.ref('password')], 'Şifreler eşleşmeli')
|
||
.required('Lütfen şifrenizi onaylayın'),
|
||
});
|
||
|
||
const RegisterPage: React.FC = () => {
|
||
const navigate = useNavigate();
|
||
const { register, isLoading } = useAuth();
|
||
const [showPassword, setShowPassword] = useState(false);
|
||
const [showConfirmPassword, setShowConfirmPassword] = useState(false);
|
||
const [error, setError] = useState<string>('');
|
||
|
||
const {
|
||
control,
|
||
handleSubmit,
|
||
formState: { errors },
|
||
} = useForm<RegisterForm>({
|
||
resolver: yupResolver(schema),
|
||
defaultValues: {
|
||
firstName: '',
|
||
lastName: '',
|
||
email: '',
|
||
password: '',
|
||
confirmPassword: '',
|
||
},
|
||
});
|
||
|
||
const onSubmit = async (data: RegisterForm) => {
|
||
try {
|
||
setError('');
|
||
await register({
|
||
firstName: data.firstName,
|
||
lastName: data.lastName,
|
||
email: data.email,
|
||
password: data.password,
|
||
confirmPassword: data.confirmPassword,
|
||
});
|
||
toast.success('Hesap başarıyla oluşturuldu!');
|
||
navigate('/dashboard');
|
||
} catch (err: any) {
|
||
const errorMessage = err.response?.data?.message || 'Kayıt başarısız. Lütfen tekrar deneyin.';
|
||
setError(errorMessage);
|
||
toast.error(errorMessage);
|
||
}
|
||
};
|
||
|
||
const handleClickShowPassword = () => {
|
||
setShowPassword(!showPassword);
|
||
};
|
||
|
||
const handleClickShowConfirmPassword = () => {
|
||
setShowConfirmPassword(!showConfirmPassword);
|
||
};
|
||
|
||
const handleGoogleLogin = () => {
|
||
// Backend'deki Google OAuth endpoint'ine yönlendir
|
||
const apiUrl = process.env.REACT_APP_API_URL || 'http://localhost:7001/api';
|
||
window.location.href = `${apiUrl}/auth/google`;
|
||
};
|
||
|
||
return (
|
||
<Container component="main" maxWidth="sm">
|
||
<Box
|
||
sx={{
|
||
marginTop: 4,
|
||
marginBottom: 4,
|
||
display: 'flex',
|
||
flexDirection: 'column',
|
||
alignItems: 'center',
|
||
}}
|
||
>
|
||
<Paper
|
||
elevation={3}
|
||
sx={{
|
||
padding: 4,
|
||
display: 'flex',
|
||
flexDirection: 'column',
|
||
alignItems: 'center',
|
||
width: '100%',
|
||
}}
|
||
>
|
||
{/* Logo and Title */}
|
||
<Box sx={{ display: 'flex', alignItems: 'center', mb: 2 }}>
|
||
<ShoppingCart sx={{ fontSize: 40, color: 'primary.main', mr: 1 }} />
|
||
<Typography component="h1" variant="h4" color="primary.main" fontWeight="bold">
|
||
hMarket
|
||
</Typography>
|
||
</Box>
|
||
|
||
<Typography component="h2" variant="h5" sx={{ mb: 3 }}>
|
||
Hesap Oluştur
|
||
</Typography>
|
||
|
||
{error && (
|
||
<Alert severity="error" sx={{ width: '100%', mb: 2 }}>
|
||
{error}
|
||
</Alert>
|
||
)}
|
||
|
||
<Box component="form" onSubmit={handleSubmit(onSubmit)} sx={{ width: '100%' }}>
|
||
<Box sx={{ display: 'grid', gridTemplateColumns: { xs: '1fr', sm: '1fr 1fr' }, gap: 2 }}>
|
||
<Box>
|
||
<Controller
|
||
name="firstName"
|
||
control={control}
|
||
render={({ field }) => (
|
||
<TextField
|
||
{...field}
|
||
required
|
||
fullWidth
|
||
id="firstName"
|
||
label="Ad"
|
||
autoComplete="given-name"
|
||
autoFocus
|
||
error={!!errors.firstName}
|
||
helperText={errors.firstName?.message}
|
||
InputProps={{
|
||
startAdornment: (
|
||
<InputAdornment position="start">
|
||
<Person />
|
||
</InputAdornment>
|
||
),
|
||
}}
|
||
/>
|
||
)}
|
||
/>
|
||
</Box>
|
||
<Box>
|
||
<Controller
|
||
name="lastName"
|
||
control={control}
|
||
render={({ field }) => (
|
||
<TextField
|
||
{...field}
|
||
required
|
||
fullWidth
|
||
id="lastName"
|
||
label="Soyad"
|
||
autoComplete="family-name"
|
||
error={!!errors.lastName}
|
||
helperText={errors.lastName?.message}
|
||
InputProps={{
|
||
startAdornment: (
|
||
<InputAdornment position="start">
|
||
<Person />
|
||
</InputAdornment>
|
||
),
|
||
}}
|
||
/>
|
||
)}
|
||
/>
|
||
</Box>
|
||
</Box>
|
||
|
||
<Controller
|
||
name="email"
|
||
control={control}
|
||
render={({ field }) => (
|
||
<TextField
|
||
{...field}
|
||
margin="normal"
|
||
required
|
||
fullWidth
|
||
id="email"
|
||
label="E-posta Adresi"
|
||
autoComplete="email"
|
||
error={!!errors.email}
|
||
helperText={errors.email?.message}
|
||
InputProps={{
|
||
startAdornment: (
|
||
<InputAdornment position="start">
|
||
<Email />
|
||
</InputAdornment>
|
||
),
|
||
}}
|
||
/>
|
||
)}
|
||
/>
|
||
|
||
<Controller
|
||
name="password"
|
||
control={control}
|
||
render={({ field }) => (
|
||
<TextField
|
||
{...field}
|
||
margin="normal"
|
||
required
|
||
fullWidth
|
||
label="Şifre"
|
||
type={showPassword ? 'text' : 'password'}
|
||
id="password"
|
||
autoComplete="new-password"
|
||
error={!!errors.password}
|
||
helperText={errors.password?.message}
|
||
InputProps={{
|
||
startAdornment: (
|
||
<InputAdornment position="start">
|
||
<Lock />
|
||
</InputAdornment>
|
||
),
|
||
endAdornment: (
|
||
<InputAdornment position="end">
|
||
<IconButton
|
||
aria-label="toggle password visibility"
|
||
onClick={handleClickShowPassword}
|
||
edge="end"
|
||
>
|
||
{showPassword ? <VisibilityOff /> : <Visibility />}
|
||
</IconButton>
|
||
</InputAdornment>
|
||
),
|
||
}}
|
||
/>
|
||
)}
|
||
/>
|
||
|
||
<Controller
|
||
name="confirmPassword"
|
||
control={control}
|
||
render={({ field }) => (
|
||
<TextField
|
||
{...field}
|
||
margin="normal"
|
||
required
|
||
fullWidth
|
||
label="Şifre Onayı"
|
||
type={showConfirmPassword ? 'text' : 'password'}
|
||
id="confirmPassword"
|
||
autoComplete="new-password"
|
||
error={!!errors.confirmPassword}
|
||
helperText={errors.confirmPassword?.message}
|
||
InputProps={{
|
||
startAdornment: (
|
||
<InputAdornment position="start">
|
||
<Lock />
|
||
</InputAdornment>
|
||
),
|
||
endAdornment: (
|
||
<InputAdornment position="end">
|
||
<IconButton
|
||
aria-label="toggle confirm password visibility"
|
||
onClick={handleClickShowConfirmPassword}
|
||
edge="end"
|
||
>
|
||
{showConfirmPassword ? <VisibilityOff /> : <Visibility />}
|
||
</IconButton>
|
||
</InputAdornment>
|
||
),
|
||
}}
|
||
/>
|
||
)}
|
||
/>
|
||
|
||
<Button
|
||
type="submit"
|
||
fullWidth
|
||
variant="contained"
|
||
sx={{ mt: 3, mb: 2, py: 1.5 }}
|
||
disabled={isLoading}
|
||
>
|
||
{isLoading ? 'Hesap Oluşturuluyor...' : 'Hesap Oluştur'}
|
||
</Button>
|
||
|
||
<Button
|
||
fullWidth
|
||
variant="outlined"
|
||
onClick={handleGoogleLogin}
|
||
disabled={isLoading}
|
||
startIcon={<Google />}
|
||
sx={{
|
||
mb: 2,
|
||
py: 1.5,
|
||
borderColor: '#db4437',
|
||
color: '#db4437',
|
||
'&:hover': {
|
||
borderColor: '#c23321',
|
||
backgroundColor: 'rgba(219, 68, 55, 0.04)',
|
||
},
|
||
}}
|
||
>
|
||
Google ile Kayıt Ol
|
||
</Button>
|
||
|
||
<Box sx={{ textAlign: 'center' }}>
|
||
<Typography variant="body2" color="text.secondary">
|
||
Zaten hesabınız var mı?{' '}
|
||
<Link
|
||
component="button"
|
||
variant="body2"
|
||
onClick={() => navigate('/login')}
|
||
sx={{ textDecoration: 'none', fontWeight: 'bold' }}
|
||
>
|
||
Giriş Yap
|
||
</Link>
|
||
</Typography>
|
||
</Box>
|
||
</Box>
|
||
</Paper>
|
||
|
||
{/* Terms */}
|
||
<Box sx={{ mt: 2, textAlign: 'center' }}>
|
||
<Typography variant="body2" color="text.secondary">
|
||
Hesap oluşturarak,{' '}
|
||
<Link
|
||
href="https://www.mustafaozkaya.tr/hmarket-kullanim-sartlari/"
|
||
target="_blank"
|
||
rel="noopener"
|
||
sx={{ textDecoration: 'none' }}
|
||
>
|
||
Kullanım Şartları
|
||
</Link>
|
||
{' '}ve{' '}
|
||
<Link
|
||
href="https://www.mustafaozkaya.tr/hmarket-gizlilik-politikasi/"
|
||
target="_blank"
|
||
rel="noopener"
|
||
sx={{ textDecoration: 'none' }}
|
||
>
|
||
Gizlilik Politikası
|
||
</Link>
|
||
'nı kabul etmiş olursunuz.
|
||
</Typography>
|
||
</Box>
|
||
</Box>
|
||
</Container>
|
||
);
|
||
};
|
||
|
||
export default RegisterPage; |