Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4,473 changes: 2,012 additions & 2,461 deletions package-lock.json

Large diffs are not rendered by default.

2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -75,10 +75,12 @@
"react-mosaic-component": "^6.1.1",
"react-resizable-panels": "^2.1.7",
"react-router-dom": "^6.26.2",
"react-smooth-corners": "^1.0.4",
"recharts": "^2.13.0",
"sonner": "^1.5.0",
"tailwind-merge": "^2.5.4",
"tailwindcss-animate": "^1.0.7",
"universal-cookie": "^8.0.1",
"usehooks-ts": "^3.1.0",
"vite-svg-loader": "^5.1.0",
"zod": "^3.23.8",
Expand Down
43 changes: 43 additions & 0 deletions public/grain.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 2 additions & 0 deletions src/app.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import '@/styles/autofill.css';
import '@/styles/font.css';
import '@/styles/index.css';
import '@/styles/scrollbar.css';
import { Oauth } from './pages/oauth';

function App() {
const { user, setTutorialStep } = useAuthStore();
Expand Down Expand Up @@ -106,6 +107,7 @@ function App() {
{/* Authentication */}
<Route path="/register" element={<Register />} />
<Route path="/login" element={<Login />} />
<Route path="/oauth" element={<Oauth />} />

{/* Connected */}
<Route
Expand Down
228 changes: 169 additions & 59 deletions src/pages/login/login.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,9 @@ import { useNavigate, Link } from 'react-router-dom';
import { useTranslation } from 'react-i18next';
import { track } from '@vercel/analytics';
import { useDeviceData } from 'react-device-detect';
// import lichessIcon from '@/assets/icons/lichess.svg?url';
import { SmoothCorners } from 'react-smooth-corners';
import { Label } from '@/components/ui/label.tsx';
import lichessIcon from '@/assets/icons/lichess.svg?url';
// import chessComIcon from '@/assets/icons/chesscom.svg?url';

/**
Expand Down Expand Up @@ -74,73 +76,181 @@ export const Login = () => {
ua,
});
navigate('/');
} catch (error) {
/* eslint-disable @typescript-eslint/no-explicit-any */
} catch (error: any) {
console.error('Failed to login:', error);

form.setError('email', { message: 'Invalid Credentials' });
form.setError('password', { message: 'Invalid Credentials' });
form.setError('email', { message: error.response.data.message });
form.setError('password', { message: error.response.data.message });
}
};

return (
<div className="flex justify-center lg:items-center bg-background h-full lg:py-20">
<div className="flex flex-col w-full px-4 lg:px-0 lg:w-96">
<h1 className="text-4xl my-8">{t('login')}</h1>
<Form {...form}>
<form onSubmit={form.handleSubmit(onSubmit)} className="flex flex-col gap-3 w-full">
<FormField
control={form.control}
name="email"
render={({ field }) => (
<FormItem>
<FormControl>
<Input autoComplete="email" placeholder={t('email')} {...field} />
</FormControl>
<FormMessage />
</FormItem>
)}
/>

<FormField
control={form.control}
name="password"
render={({ field }) => (
<FormItem>
<FormControl>
<Input type="password" autoComplete="current-password" placeholder={t('password')} {...field} />
</FormControl>
<FormMessage />
</FormItem>
)}
/>

<div className="flex justify-between gap-6 items-center">
<a className="text-white text-sm underline" href="/">
{t('forgotPassword')}{' '}
</a>
<Button type="submit">{t('login')}</Button>
</div>
</form>
</Form>

<div className="mt-10 text-sm w-full">
{t('noAccount')}{' '}
<Link to="/register" className="underline hover:text-[#EC9E67]">
{t('register')}
</Link>
</div>
<div className="justify-center h-full flex px-10 md:py-10 py-4 items-center">
<div className="container px-0 sm:px-10 md:px-20 lg:px-40 xl:px-0 flex flex-col lg:flex-row gap-32 h-full max-h-[50rem]">
{/* @ts-expect-error - SmoothCorners is not typed */}
<SmoothCorners corners="14" className="xl:flex hidden w-1/2 h-full bg-[url('/grain.svg')] bg-cover bg-center">
<div className="flex flex-col gap-6 items-center mt-[50%] w-full">
<span className="text-xl items-center text-center flex gap-2">
<img src="/logo.svg" alt="castled-logo" className="h-8" />
<p className="mt-auto leading-[18px]">Castled</p>
</span>
<h1 className="text-4xl text-center">Get Started</h1>

<p>Sign-in and start analysing your chess games</p>
</div>
</SmoothCorners>

<div className="w-full xl:w-1/2 flex md:py-20 py-10 flex-col gap-14 items-center">
<span className="text-center space-y-3">
<h1 className="text-3xl">{t('login')}</h1>
<p className="text-foreground/70">Sign in to an existing account</p>
</span>

{/*<div className="flex mt-10 space-x-4 w-full">*/}
{/* <Button variant="secondary" className="w-full">*/}
{/* <img src={lichessIcon} alt="Lichess" className="h-6" />*/}
{/* <span>Lichess</span>*/}
{/* </Button>*/}

{/* <Button variant="secondary" className="w-full">*/}
{/* <img src={chessComIcon} alt="Chess.com" className="h-6" />*/}
{/* </Button>*/}
{/*</div>*/}

{/*<div className="flex mt-4 space-x-4 w-full">*/}
{/* <Button variant="secondary" className="w-full">*/}
{/* <img src={lichessIcon} alt="Lichess" className="h-6" />*/}
{/* <span>Lichess</span>*/}
{/* </Button>*/}
<Form {...form}>
<form onSubmit={form.handleSubmit(onSubmit)} className="flex flex-col gap-6 w-full">
<FormField
control={form.control}
name="email"
render={({ field }) => (
<FormItem>
<Label htmlFor="email" className="text-sm text-foreground">
{t('email')}
</Label>
<FormControl>
<Input
className="h-14 bg-secondary-bg border-none"
autoComplete="email"
id={'email'}
placeholder={'johndoe@gmail.com'}
{...field}
/>
</FormControl>
<FormMessage />
</FormItem>
)}
/>

{/* <Button variant="secondary" className="w-full">*/}
{/* <img src={chessComIcon} alt="Chess.com" className="h-6" />*/}
{/* </Button>*/}
{/*</div>*/}
<FormField
control={form.control}
name="password"
render={({ field }) => (
<FormItem className="w-full">
<Label htmlFor="password" className="text-sm text-foreground">
{t('password')}
</Label>
<FormControl>
<Input
className="h-14 bg-secondary-bg border-none"
type="password"
id="password"
autoComplete="new-password"
placeholder={t('password')}
{...field}
/>
</FormControl>
<FormMessage />
</FormItem>
)}
/>

<Button
type="submit"
className="h-14 bg-foreground hover:bg-foreground/90 text-background text-lg font-semibold"
>
{t('login')}
</Button>

<Link to={`${import.meta.env.VITE_API_URL}/api/v1/auth/lichess/login`}>
<Button variant="secondary" type="button" className="h-14 w-full text-lg">
<img src={lichessIcon} alt="Lichess" className="h-6" />
{t('login')} with Lichess
</Button>
</Link>

<div className="text-sm w-full text-center">
{t('noAccount')}{' '}
<Link to="/register" className="underline hover:text-[#EC9E67]">
{t('register')}
</Link>
</div>
</form>
</Form>
</div>
</div>
</div>
);

// return (
// <div className="flex justify-center lg:items-center bg-background h-full lg:py-20">
// <div className="flex flex-col w-full px-4 lg:px-0 lg:w-96">
// <h1 className="text-4xl my-8">{t('login')}</h1>
// <Form {...form}>
// <form onSubmit={form.handleSubmit(onSubmit)} className="flex flex-col gap-3 w-full">
// <FormField
// control={form.control}
// name="email"
// render={({ field }) => (
// <FormItem>
// <FormControl>
// <Input autoComplete="email" placeholder={t('email')} {...field} />
// </FormControl>
// <FormMessage />
// </FormItem>
// )}
// />
//
// <FormField
// control={form.control}
// name="password"
// render={({ field }) => (
// <FormItem>
// <FormControl>
// <Input type="password" autoComplete="current-password" placeholder={t('password')} {...field} />
// </FormControl>
// <FormMessage />
// </FormItem>
// )}
// />
//
// <div className="flex justify-between gap-6 items-center">
// <a className="text-white text-sm underline" href="/">
// {t('forgotPassword')}{' '}
// </a>
// <Button type="submit">{t('login')}</Button>
// </div>
// </form>
// </Form>
//
// <div className="mt-10 text-sm w-full">
// {t('noAccount')}{' '}
// <Link to="/register" className="underline hover:text-[#EC9E67]">
// {t('register')}
// </Link>
// </div>
//
// {/*<div className="flex mt-4 space-x-4 w-full">*/}
// {/* <Button variant="secondary" className="w-full">*/}
// {/* <img src={lichessIcon} alt="Lichess" className="h-6" />*/}
// {/* <span>Lichess</span>*/}
// {/* </Button>*/}
//
// {/* <Button variant="secondary" className="w-full">*/}
// {/* <img src={chessComIcon} alt="Chess.com" className="h-6" />*/}
// {/* </Button>*/}
// {/*</div>*/}
// </div>
// </div>
// );
};
35 changes: 35 additions & 0 deletions src/pages/oauth.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import { useAuthStore } from '@/store/auth';
import { Icon } from '@iconify/react/dist/iconify.js';
import { useEffect } from 'react';
import { useNavigate } from 'react-router-dom';

export const Oauth = () => {
const { setUser, setAccessToken, setRefreshToken } = useAuthStore();
const navigate = useNavigate();

function getCookie(key: string) {
var b = document.cookie.match('(^|;)\\s*' + key + '\\s*=\\s*([^;]+)');
return b ? b.pop() : '';
}

useEffect(() => {
const access = getCookie('lichess_access_token') as string;
const refresh = getCookie('lichess_refresh_token') as string;
const user = getCookie('lichess_user') as string;

setUser(JSON.parse(decodeURIComponent(user)));
setAccessToken(access);
setRefreshToken(refresh);

navigate('/');
}, []);

return (
<div className="w-full h-full flex items-center justify-center">
<div className="flex flex-col gap-6 items-center">
<h1 className="text-xl">Logging you in</h1>
<Icon icon="line-md:loading-loop" width={40} />
</div>
</div>
);
};
Loading
Loading