mirror of
https://gitlab.com/foxixus/neomovies.git
synced 2025-10-28 01:48:50 +05:00
Update 103 files
- /public/file.svg - /public/globe.svg - /public/next.svg - /public/vercel.svg - /public/window.svg - /public/google.svg - /public/logo.png - /src/eslint.config.mjs - /src/api.ts - /src/middleware.ts - /src/app/favicon.ico - /src/app/globals.css - /src/app/layout.tsx - /src/app/page.tsx - /src/app/providers.tsx - /src/app/not-found.tsx - /src/app/error.tsx - /src/app/metadata.ts - /src/app/styles.tsx - /src/app/api/auth/[...nextauth]/route.ts - /src/app/api/auth/register/route.ts - /src/app/api/auth/verify/route.ts - /src/app/api/auth/check-verification/route.ts - /src/app/api/auth/resend-code/route.ts - /src/app/api/movies/search/route.ts - /src/app/api/movies/sync/route.ts - /src/app/api/admin/send-verification/route.ts - /src/app/api/admin/verify-code/route.ts - /src/app/api/admin/movies/route.ts - /src/app/api/admin/movies/toggle-visibility/route.ts - /src/app/api/admin/create/route.ts - /src/app/api/admin/users/toggle-admin/route.ts - /src/app/api/admin/toggle-admin/route.ts - /src/app/login/page.tsx - /src/app/login/LoginClient.tsx - /src/app/verify/page.tsx - /src/app/verify/VerificationClient.tsx - /src/app/profile/page.tsx - /src/app/movie/[id]/page.tsx - /src/app/movie/[id]/MoviePage.tsx - /src/app/movie/[id]/MovieContent.tsx - /src/app/settings/page.tsx - /src/app/tv/[id]/page.tsx - /src/app/tv/[id]/TVShowPage.tsx - /src/app/tv/[id]/TVShowContent.tsx - /src/app/admin/login/page.tsx - /src/app/admin/login/AdminLoginClient.tsx - /src/lib/db.ts - /src/lib/jwt.ts - /src/lib/registry.tsx - /src/lib/api.ts - /src/lib/mongodb.ts - /src/lib/mailer.ts - /src/lib/auth.ts - /src/lib/utils.ts - /src/lib/email.ts - /src/lib/movieSync.ts - /src/models/User.ts - /src/models/index.ts - /src/models/Movie.ts - /src/types/auth.ts - /src/types/movie.ts - /src/components/MovieCard.tsx - /src/components/Notification.tsx - /src/components/Pagination.tsx - /src/components/GoogleIcon.tsx - /src/components/StyleProvider.tsx - /src/components/Providers.tsx - /src/components/VerificationCodeInput.tsx - /src/components/GlassCard.tsx - /src/components/AppLayout.tsx - /src/components/SearchModal.tsx - /src/components/DarkReaderFix.tsx - /src/components/ClientLayout.tsx - /src/components/MenuItem.tsx - /src/components/MoviePlayer.tsx - /src/components/PageLayout.tsx - /src/components/SettingsContent.tsx - /src/components/Navbar.tsx - /src/components/LayoutContent.tsx - /src/components/SearchResults.tsx - /src/components/Icons/Icons.tsx - /src/components/Icons/HeartIcon.tsx - /src/components/Icons/PlayIcon.tsx - /src/components/admin/MovieSearch.tsx - /src/hooks/useUser.ts - /src/hooks/useMovies.ts - /src/hooks/useSettings.ts - /src/hooks/useSearch.ts - /src/styles/GlobalStyles.ts - /src/styles/GlobalStyles.tsx - /src/providers/AuthProvider.tsx - /src/data/movies.ts - /types/next-auth.d.ts - /middleware.ts - /next.config.js - /next-env.d.ts - /package.json - /postcss.config.mjs - /README.md - /tailwind.config.ts - /tsconfig.json - /package-lock.json
This commit is contained in:
317
src/app/login/LoginClient.tsx
Normal file
317
src/app/login/LoginClient.tsx
Normal file
@@ -0,0 +1,317 @@
|
||||
'use client';
|
||||
|
||||
import React, { useState } from 'react';
|
||||
import styled from 'styled-components';
|
||||
import { signIn } from 'next-auth/react';
|
||||
import { useRouter } from 'next/navigation';
|
||||
|
||||
const Container = styled.div`
|
||||
width: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 2rem;
|
||||
`;
|
||||
|
||||
const Form = styled.form`
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 1.5rem;
|
||||
width: 100%;
|
||||
`;
|
||||
|
||||
const Title = styled.h2`
|
||||
font-size: 2rem;
|
||||
font-weight: 700;
|
||||
color: #fff;
|
||||
text-align: center;
|
||||
margin-bottom: 0.5rem;
|
||||
`;
|
||||
|
||||
const Subtitle = styled.p`
|
||||
color: rgba(255, 255, 255, 0.7);
|
||||
text-align: center;
|
||||
margin-bottom: 2rem;
|
||||
font-size: 1rem;
|
||||
`;
|
||||
|
||||
const InputGroup = styled.div`
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.5rem;
|
||||
width: 100%;
|
||||
`;
|
||||
|
||||
const Label = styled.label`
|
||||
font-size: 0.875rem;
|
||||
font-weight: 500;
|
||||
color: rgba(255, 255, 255, 0.9);
|
||||
`;
|
||||
|
||||
const Input = styled.input`
|
||||
width: 100%;
|
||||
padding: 1rem;
|
||||
border-radius: 12px;
|
||||
border: 1px solid rgba(255, 255, 255, 0.1);
|
||||
background: rgba(255, 255, 255, 0.05);
|
||||
color: #fff;
|
||||
font-size: 1rem;
|
||||
transition: all 0.2s;
|
||||
|
||||
&::placeholder {
|
||||
color: rgba(255, 255, 255, 0.3);
|
||||
}
|
||||
|
||||
&:focus {
|
||||
outline: none;
|
||||
border-color: #2196f3;
|
||||
background: rgba(255, 255, 255, 0.1);
|
||||
box-shadow: 0 0 0 4px rgba(33, 150, 243, 0.1);
|
||||
}
|
||||
`;
|
||||
|
||||
const Button = styled.button`
|
||||
width: 100%;
|
||||
background: linear-gradient(to right, #2196f3, #1e88e5);
|
||||
color: white;
|
||||
padding: 1rem;
|
||||
border-radius: 12px;
|
||||
border: none;
|
||||
font-size: 1rem;
|
||||
font-weight: 600;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s;
|
||||
margin-top: 1rem;
|
||||
|
||||
&:hover {
|
||||
background: linear-gradient(to right, #1e88e5, #1976d2);
|
||||
transform: translateY(-1px);
|
||||
box-shadow: 0 4px 15px rgba(33, 150, 243, 0.3);
|
||||
}
|
||||
|
||||
&:active {
|
||||
transform: translateY(0);
|
||||
}
|
||||
`;
|
||||
|
||||
const Divider = styled.div`
|
||||
display: flex;
|
||||
align-items: center;
|
||||
text-align: center;
|
||||
margin: 2rem 0;
|
||||
|
||||
&::before,
|
||||
&::after {
|
||||
content: '';
|
||||
flex: 1;
|
||||
border-bottom: 1px solid rgba(255, 255, 255, 0.1);
|
||||
}
|
||||
|
||||
&::before {
|
||||
margin-right: 1rem;
|
||||
}
|
||||
|
||||
&::after {
|
||||
margin-left: 1rem;
|
||||
}
|
||||
`;
|
||||
|
||||
const DividerText = styled.span`
|
||||
color: rgba(255, 255, 255, 0.5);
|
||||
font-size: 0.875rem;
|
||||
padding: 0 1rem;
|
||||
`;
|
||||
|
||||
const GoogleButton = styled(Button)`
|
||||
background: rgba(255, 255, 255, 0.05);
|
||||
border: 1px solid rgba(255, 255, 255, 0.1);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 0.5rem;
|
||||
margin-top: 0;
|
||||
|
||||
&:hover {
|
||||
background: rgba(255, 255, 255, 0.1);
|
||||
box-shadow: 0 4px 15px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
`;
|
||||
|
||||
const ToggleText = styled.p`
|
||||
color: rgba(255, 255, 255, 0.7);
|
||||
text-align: center;
|
||||
margin-top: 2rem;
|
||||
|
||||
button {
|
||||
color: #2196f3;
|
||||
background: none;
|
||||
border: none;
|
||||
padding: 0;
|
||||
margin-left: 0.5rem;
|
||||
font-weight: 600;
|
||||
cursor: pointer;
|
||||
|
||||
&:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
const ErrorMessage = styled.div`
|
||||
color: #ff5252;
|
||||
font-size: 0.875rem;
|
||||
text-align: center;
|
||||
padding: 0.75rem;
|
||||
background: rgba(255, 82, 82, 0.1);
|
||||
border-radius: 8px;
|
||||
margin-top: 1rem;
|
||||
`;
|
||||
|
||||
export default function LoginClient() {
|
||||
const [isLogin, setIsLogin] = useState(true);
|
||||
const [email, setEmail] = useState('');
|
||||
const [password, setPassword] = useState('');
|
||||
const [name, setName] = useState('');
|
||||
const [error, setError] = useState('');
|
||||
const router = useRouter();
|
||||
|
||||
const handleSubmit = async (e: React.FormEvent) => {
|
||||
e.preventDefault();
|
||||
setError('');
|
||||
|
||||
try {
|
||||
if (isLogin) {
|
||||
const result = await signIn('credentials', {
|
||||
redirect: false,
|
||||
email,
|
||||
password,
|
||||
});
|
||||
|
||||
if (result?.error) {
|
||||
if (result.error === 'EMAIL_NOT_VERIFIED') {
|
||||
router.push(`/verify?email=${encodeURIComponent(email)}`);
|
||||
return;
|
||||
}
|
||||
throw new Error(result.error);
|
||||
}
|
||||
|
||||
router.push('/');
|
||||
} else {
|
||||
const response = await fetch('/api/auth/register', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ email, password, name }),
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
const data = await response.json();
|
||||
throw new Error(data.error || 'Ошибка при регистрации');
|
||||
}
|
||||
|
||||
const data = await response.json();
|
||||
|
||||
// Сохраняем пароль для автовхода после верификации
|
||||
localStorage.setItem('password', password);
|
||||
|
||||
router.push(`/verify?email=${encodeURIComponent(email)}`);
|
||||
}
|
||||
} catch (err) {
|
||||
setError(err instanceof Error ? err.message : 'Произошла ошибка');
|
||||
}
|
||||
};
|
||||
|
||||
const handleGoogleSignIn = () => {
|
||||
signIn('google', { callbackUrl: '/' });
|
||||
};
|
||||
|
||||
return (
|
||||
<Container>
|
||||
<div>
|
||||
<Title>{isLogin ? 'С возвращением!' : 'Создать аккаунт'}</Title>
|
||||
<Subtitle>
|
||||
{isLogin
|
||||
? 'Войдите в свой аккаунт для доступа к фильмам'
|
||||
: 'Зарегистрируйтесь для доступа ко всем возможностям'}
|
||||
</Subtitle>
|
||||
</div>
|
||||
|
||||
<Form onSubmit={handleSubmit}>
|
||||
{!isLogin && (
|
||||
<InputGroup>
|
||||
<Label htmlFor="name">Имя</Label>
|
||||
<Input
|
||||
id="name"
|
||||
type="text"
|
||||
placeholder="Введите ваше имя"
|
||||
value={name}
|
||||
onChange={(e) => setName(e.target.value)}
|
||||
required={!isLogin}
|
||||
/>
|
||||
</InputGroup>
|
||||
)}
|
||||
|
||||
<InputGroup>
|
||||
<Label htmlFor="email">Email</Label>
|
||||
<Input
|
||||
id="email"
|
||||
type="email"
|
||||
placeholder="Введите ваш email"
|
||||
value={email}
|
||||
onChange={(e) => setEmail(e.target.value)}
|
||||
required
|
||||
/>
|
||||
</InputGroup>
|
||||
|
||||
<InputGroup>
|
||||
<Label htmlFor="password">Пароль</Label>
|
||||
<Input
|
||||
id="password"
|
||||
type="password"
|
||||
placeholder="Введите ваш пароль"
|
||||
value={password}
|
||||
onChange={(e) => setPassword(e.target.value)}
|
||||
required
|
||||
/>
|
||||
</InputGroup>
|
||||
|
||||
{error && <ErrorMessage>{error}</ErrorMessage>}
|
||||
|
||||
<Button type="submit">
|
||||
{isLogin ? 'Войти' : 'Зарегистрироваться'}
|
||||
</Button>
|
||||
</Form>
|
||||
|
||||
<Divider>
|
||||
<DividerText>или</DividerText>
|
||||
</Divider>
|
||||
|
||||
<GoogleButton type="button" onClick={handleGoogleSignIn}>
|
||||
<svg width="18" height="18" viewBox="0 0 18 18" fill="none">
|
||||
<path
|
||||
d="M17.64 9.2c0-.637-.057-1.251-.164-1.84H9v3.481h4.844c-.209 1.125-.843 2.078-1.796 2.717v2.258h2.908c1.702-1.567 2.684-3.874 2.684-6.615z"
|
||||
fill="#4285f4"
|
||||
/>
|
||||
<path
|
||||
d="M9 18c2.43 0 4.467-.806 5.956-2.184l-2.908-2.258c-.806.54-1.837.86-3.048.86-2.344 0-4.328-1.584-5.036-3.711H.957v2.332A8.997 8.997 0 0 0 9 18z"
|
||||
fill="#34a853"
|
||||
/>
|
||||
<path
|
||||
d="M3.964 10.707A5.41 5.41 0 0 1 3.682 9c0-.593.102-1.17.282-1.707V4.961H.957A8.996 8.996 0 0 0 0 9c0 1.452.348 2.827.957 4.046l3.007-2.339z"
|
||||
fill="#fbbc05"
|
||||
/>
|
||||
<path
|
||||
d="M9 3.582c1.321 0 2.508.454 3.44 1.345l2.582-2.58C13.463.891 11.426 0 9 0A8.997 8.997 0 0 0 .957 4.961L3.964 7.3C4.672 5.173 6.656 3.582 9 3.582z"
|
||||
fill="#ea4335"
|
||||
/>
|
||||
</svg>
|
||||
Продолжить с Google
|
||||
</GoogleButton>
|
||||
|
||||
<ToggleText>
|
||||
{isLogin ? 'Еще нет аккаунта?' : 'Уже есть аккаунт?'}
|
||||
<button type="button" onClick={() => setIsLogin(!isLogin)}>
|
||||
{isLogin ? 'Зарегистрироваться' : 'Войти'}
|
||||
</button>
|
||||
</ToggleText>
|
||||
</Container>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user