Files
hMarket/frontend/src/pages/Auth/RegisterPage.tsx

381 lines
12 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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;