hMarket Trae ilk versiyon

This commit is contained in:
hOLOlu
2026-02-03 01:22:08 +03:00
commit 2b861156fe
74 changed files with 42127 additions and 0 deletions

View File

@@ -0,0 +1,370 @@
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">
By creating an account, you agree to our{' '}
<Link href="#" sx={{ textDecoration: 'none' }}>
Terms of Service
</Link>{' '}
and{' '}
<Link href="#" sx={{ textDecoration: 'none' }}>
Privacy Policy
</Link>
</Typography>
</Box>
</Box>
</Container>
);
};
export default RegisterPage;