main
Саске Учиха 2 years ago
parent 4b895006c8
commit 50b72cd033

@ -3,11 +3,16 @@
Ararat International School - платформа по обучению игры в шахматы.
### Стэк:
- Tailwind css
- React.js
- Express.js
- Tailwind css
- TypeScript
- Mobx (временно)
#### V 0.01
- Добавлена главная страница
- Регистрация и авторизация ведет на главую страницу
- Форма регистрации/авторизации из управляемых input теперь работает при помощи react-hook-form
#### V 0.0
- Инициализация

@ -4,6 +4,9 @@
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<link rel="preconnect" href="https://fonts.googleapis.com"/>
<link rel="preconnect" href="https://fonts.gstatic.com" crossOrigin="true"/>
<link href="https://fonts.googleapis.com/css2?family=Manrope:wght@400;500;600;700&display=swap" rel="stylesheet"/>
<title>AraratChess</title>
</head>
<body class="bg-gray-100">

@ -9,11 +9,14 @@
"version": "0.0.0",
"dependencies": {
"@types/axios": "^0.14.0",
"@types/react-router-dom": "^5.3.3",
"axios": "^1.4.0",
"mobx": "^6.9.1",
"mobx-react-lite": "^4.0.3",
"react": "^18.2.0",
"react-dom": "^18.2.0"
"react-dom": "^18.2.0",
"react-hook-form": "^7.45.2",
"react-router-dom": "^6.14.2"
},
"devDependencies": {
"@types/react": "^18.2.14",
@ -957,6 +960,14 @@
"node": ">= 8"
}
},
"node_modules/@remix-run/router": {
"version": "1.7.2",
"resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.7.2.tgz",
"integrity": "sha512-7Lcn7IqGMV+vizMPoEl5F0XDshcdDYtMI6uJLQdQz5CfZAwy3vvGKYSUk789qndt5dEC4HfSjviSYlSoHGL2+A==",
"engines": {
"node": ">=14"
}
},
"node_modules/@types/axios": {
"version": "0.14.0",
"resolved": "https://registry.npmjs.org/@types/axios/-/axios-0.14.0.tgz",
@ -966,6 +977,11 @@
"axios": "*"
}
},
"node_modules/@types/history": {
"version": "4.7.11",
"resolved": "https://registry.npmjs.org/@types/history/-/history-4.7.11.tgz",
"integrity": "sha512-qjDJRrmvBMiTx+jyLxvLfJU7UznFuokDv4f3WRuriHKERccVpFU+8XMQUAbDzoiJCsmexxRExQeMwwCdamSKDA=="
},
"node_modules/@types/json-schema": {
"version": "7.0.12",
"resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.12.tgz",
@ -975,14 +991,12 @@
"node_modules/@types/prop-types": {
"version": "15.7.5",
"resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.5.tgz",
"integrity": "sha512-JCB8C6SnDoQf0cNycqd/35A7MjcnK+ZTqE7judS6o7utxUCg6imJg3QK2qzHKszlTjcj2cn+NwMB2i96ubpj7w==",
"dev": true
"integrity": "sha512-JCB8C6SnDoQf0cNycqd/35A7MjcnK+ZTqE7judS6o7utxUCg6imJg3QK2qzHKszlTjcj2cn+NwMB2i96ubpj7w=="
},
"node_modules/@types/react": {
"version": "18.2.15",
"resolved": "https://registry.npmjs.org/@types/react/-/react-18.2.15.tgz",
"integrity": "sha512-oEjE7TQt1fFTFSbf8kkNuc798ahTUzn3Le67/PWjE8MAfYAD/qB7O8hSTcromLFqHCt9bcdOg5GXMokzTjJ5SA==",
"dev": true,
"dependencies": {
"@types/prop-types": "*",
"@types/scheduler": "*",
@ -998,11 +1012,29 @@
"@types/react": "*"
}
},
"node_modules/@types/react-router": {
"version": "5.1.20",
"resolved": "https://registry.npmjs.org/@types/react-router/-/react-router-5.1.20.tgz",
"integrity": "sha512-jGjmu/ZqS7FjSH6owMcD5qpq19+1RS9DeVRqfl1FeBMxTDQAGwlMWOcs52NDoXaNKyG3d1cYQFMs9rCrb88o9Q==",
"dependencies": {
"@types/history": "^4.7.11",
"@types/react": "*"
}
},
"node_modules/@types/react-router-dom": {
"version": "5.3.3",
"resolved": "https://registry.npmjs.org/@types/react-router-dom/-/react-router-dom-5.3.3.tgz",
"integrity": "sha512-kpqnYK4wcdm5UaWI3fLcELopqLrHgLqNsdpHauzlQktfkHL3npOSwtj1Uz9oKBAzs7lFtVkV8j83voAz2D8fhw==",
"dependencies": {
"@types/history": "^4.7.11",
"@types/react": "*",
"@types/react-router": "*"
}
},
"node_modules/@types/scheduler": {
"version": "0.16.3",
"resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.3.tgz",
"integrity": "sha512-5cJ8CB4yAx7BH1oMvdU0Jh9lrEXyPkar6F9G/ERswkCuvP4KQZfZkSjcMbAICCpQTN4OuZn8tz0HiKv9TGZgrQ==",
"dev": true
"integrity": "sha512-5cJ8CB4yAx7BH1oMvdU0Jh9lrEXyPkar6F9G/ERswkCuvP4KQZfZkSjcMbAICCpQTN4OuZn8tz0HiKv9TGZgrQ=="
},
"node_modules/@types/semver": {
"version": "7.5.0",
@ -1598,8 +1630,7 @@
"node_modules/csstype": {
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.2.tgz",
"integrity": "sha512-I7K1Uu0MBPzaFKg4nI5Q7Vs2t+3gWWW648spaF+Rg7pI9ds18Ugn+lvg4SHczUdKlHI5LWBXyqfS8+DufyBsgQ==",
"dev": true
"integrity": "sha512-I7K1Uu0MBPzaFKg4nI5Q7Vs2t+3gWWW648spaF+Rg7pI9ds18Ugn+lvg4SHczUdKlHI5LWBXyqfS8+DufyBsgQ=="
},
"node_modules/debug": {
"version": "4.3.4",
@ -3078,6 +3109,21 @@
"react": "^18.2.0"
}
},
"node_modules/react-hook-form": {
"version": "7.45.2",
"resolved": "https://registry.npmjs.org/react-hook-form/-/react-hook-form-7.45.2.tgz",
"integrity": "sha512-9s45OdTaKN+4NSTbXVqeDITd/nwIg++nxJGL8+OD5uf1DxvhsXQ641kaYHk5K28cpIOTYm71O/fYk7rFaygb3A==",
"engines": {
"node": ">=12.22.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/react-hook-form"
},
"peerDependencies": {
"react": "^16.8.0 || ^17 || ^18"
}
},
"node_modules/react-refresh": {
"version": "0.14.0",
"resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.14.0.tgz",
@ -3087,6 +3133,36 @@
"node": ">=0.10.0"
}
},
"node_modules/react-router": {
"version": "6.14.2",
"resolved": "https://registry.npmjs.org/react-router/-/react-router-6.14.2.tgz",
"integrity": "sha512-09Zss2dE2z+T1D03IheqAFtK4UzQyX8nFPWx6jkwdYzGLXd5ie06A6ezS2fO6zJfEb/SpG6UocN2O1hfD+2urQ==",
"dependencies": {
"@remix-run/router": "1.7.2"
},
"engines": {
"node": ">=14"
},
"peerDependencies": {
"react": ">=16.8"
}
},
"node_modules/react-router-dom": {
"version": "6.14.2",
"resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.14.2.tgz",
"integrity": "sha512-5pWX0jdKR48XFZBuJqHosX3AAHjRAzygouMTyimnBPOLdY3WjzUSKhus2FVMihUFWzeLebDgr4r8UeQFAct7Bg==",
"dependencies": {
"@remix-run/router": "1.7.2",
"react-router": "6.14.2"
},
"engines": {
"node": ">=14"
},
"peerDependencies": {
"react": ">=16.8",
"react-dom": ">=16.8"
}
},
"node_modules/read-cache": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz",

@ -11,11 +11,14 @@
},
"dependencies": {
"@types/axios": "^0.14.0",
"@types/react-router-dom": "^5.3.3",
"axios": "^1.4.0",
"mobx": "^6.9.1",
"mobx-react-lite": "^4.0.3",
"react": "^18.2.0",
"react-dom": "^18.2.0"
"react-dom": "^18.2.0",
"react-hook-form": "^7.45.2",
"react-router-dom": "^6.14.2"
},
"devDependencies": {
"@types/react": "^18.2.14",

@ -0,0 +1,4 @@
<svg width="60" height="60" viewBox="0 0 60 60" fill="none" xmlns="http://www.w3.org/2000/svg">
<circle cx="30" cy="30" r="30" fill="#D9D9D9"/>
<path d="M29.5 11.1667C31.9311 11.1667 34.2627 12.1324 35.9818 13.8515C37.7009 15.5706 38.6666 17.9022 38.6666 20.3333C38.6666 22.7645 37.7009 25.0961 35.9818 26.8151C34.2627 28.5342 31.9311 29.5 29.5 29.5C27.0688 29.5 24.7372 28.5342 23.0181 26.8151C21.2991 25.0961 20.3333 22.7645 20.3333 20.3333C20.3333 17.9022 21.2991 15.5706 23.0181 13.8515C24.7372 12.1324 27.0688 11.1667 29.5 11.1667ZM29.5 34.0833C39.6291 34.0833 47.8333 38.1854 47.8333 43.25V47.8333H11.1666V43.25C11.1666 38.1854 19.3708 34.0833 29.5 34.0833Z" fill="#585858"/>
</svg>

After

Width:  |  Height:  |  Size: 689 B

@ -1,9 +1,38 @@
import RegisterForm from './components/RegisterForm'
import { useEffect, useContext } from "react"
import RequireAuth from "./components/Auth/RequireAuth"
import OnlyUnauthorized from "./components/Auth/OnlyUnauthorized"
import IndexPage from "./pages/IndexPage"
import LoginForm from "./components/LoginForm"
import RegisterForm from "./components/RegisterForm"
import { BrowserRouter, Routes, Route } from 'react-router-dom'
import { Context } from "./main"
import { observer } from "mobx-react-lite"
function App() {
return (
<RegisterForm/>
)
const {store} = useContext(Context);
useEffect(()=>{
const CheckLogin = async () => {
if(localStorage.getItem('token')) {
await store.checkAuth();
} else {
store.storeLoad();
}
}
CheckLogin().catch(console.error);
}, [])
if(!store.isLoading) {
return (
<BrowserRouter>
<Routes>
<Route path='/' element={<RequireAuth><IndexPage/></RequireAuth>}/>
<Route path='/login' element={<OnlyUnauthorized><LoginForm/></OnlyUnauthorized>}/>
<Route path='/register' element={<OnlyUnauthorized><RegisterForm/></OnlyUnauthorized>}/>
</Routes>
</BrowserRouter>
)
}
}
export default App
export default observer(App)

@ -0,0 +1,18 @@
import { FC, PropsWithChildren, useContext } from 'react'
import { useLocation, Navigate } from 'react-router-dom';
import { Context } from "../../main"
import { observer } from "mobx-react-lite"
const OnlyUnauthorized: FC<PropsWithChildren> = observer(({children}) => {
const {store} = useContext(Context);
const location = useLocation();
if (store.isAuth) {
return <Navigate to="/" state={{ from: location }} replace />;
}
return children;
})
export default OnlyUnauthorized;

@ -0,0 +1,18 @@
import { FC, PropsWithChildren, useContext } from 'react'
import { useLocation, Navigate } from 'react-router-dom';
import { Context } from "../../main"
import { observer } from "mobx-react-lite"
const RequireAuth: FC<PropsWithChildren> = observer(({children}) => {
const {store} = useContext(Context);
const location = useLocation();
if (!store.isAuth) {
return <Navigate to="/login" state={{ from: location }} replace />;
}
return children;
})
export default RequireAuth;

@ -0,0 +1,28 @@
import { FC, useContext } from 'react'
import MenuItem from './MenuItem';
import { Link } from 'react-router-dom';
import { Context } from '../../../main';
const Menu: FC = () => {
const {store} = useContext(Context);
return (
<nav>
<ul className='border-b pb-5'>
<MenuItem to='/'>Главная</MenuItem>
<MenuItem to='/1'>Мессенджер</MenuItem>
<MenuItem to='/2'>Программа</MenuItem>
<MenuItem to='/3'>Онлайн урок</MenuItem>
<MenuItem to='/4'>Домашнее задание</MenuItem>
<MenuItem to='/5'>Тренировка</MenuItem>
<MenuItem to='/6'>Лиги</MenuItem>
<MenuItem to='/7'>Турнир</MenuItem>
</ul>
<ul className='pt-5'>
<li className='mb-2'><Link to='/' className='text-slate-400'>Настройки</Link></li>
<li className='mb-2 text-slate-400 cursor-pointer' onClick={() => void store.logout()}>Выход</li>
</ul>
</nav>
)
}
export default Menu;

@ -0,0 +1,18 @@
import { FC, PropsWithChildren } from 'react'
import { Link } from 'react-router-dom';
import { useLocation } from 'react-router-dom';
interface MenuItemProps {
to: string,
}
const MenuItem: FC<PropsWithChildren<MenuItemProps>> = ({to, children}) => {
const location = useLocation();
return (
<li className={['mb-4', location.pathname === to ? 'relative before:bg-[#fbceb1] before:absolute before:w-[5px] before:h-full before:block before:top-0 before:-left-5': null].join(' ')}><Link to={to} className={['text-slate-500 hover:text-[#fbceb1] transition-all text-lg font-medium', location.pathname === to ? 'text-[#fbceb1]' : null].join(' ')}>{children}</Link></li>
)
}
export default MenuItem;

@ -0,0 +1,16 @@
import { FC } from 'react'
import UserInfo from '../UserInfo';
import Menu from './Menu';
import Logo from '../../../assets/logo.png'
const Sidebar: FC = () => {
return (
<aside className='flex flex-col bg-white pt-10 px-14 h-full shadow-lg'>
<img className='w-40 self-center mb-10' src={Logo} alt="logo" />
<UserInfo/>
<Menu/>
</aside>
)
}
export default Sidebar;

@ -0,0 +1,21 @@
import { useContext } from "react"
import { FC } from 'react'
import { Context } from "../../main"
import { observer } from "mobx-react-lite"
const UserInfo: FC = observer(() => {
const {store} = useContext(Context);
return (
<div className='flex items-center mb-12'>
<div className="mr-3 w-12 h-12"><img src={store.user.avatar} className='w-[inherit] h-[inherit]' alt="avatar"/></div>
<div className="">
<p className='text-lg font-medium'>{store.user.name + ' ' + store.user.sname}</p>
<p className='text-base'>{store.user.email}</p>
</div>
</div>
)
})
export default UserInfo;

@ -1,30 +1,45 @@
/* eslint-disable @typescript-eslint/no-misused-promises */
import {FC, useState, useContext} from 'react'
import {FC, useContext} from 'react'
import { useForm, SubmitHandler } from "react-hook-form";
import { Context } from '../main';
import Logo from '../assets/logo.png'
import { Link, useNavigate } from 'react-router-dom';
type Form = {
email: string,
password: string,
};
const LoginForm: FC = () => {
const [email, setEmail] = useState<string>('');
const [password, setPassword] = useState<string>('');
const navigate = useNavigate();
const {store} = useContext(Context);
const { register, handleSubmit } = useForm<Form>();
const onSubmit: SubmitHandler<Form> = async (data) => {
await store.login(data.email, data.password);
if(store.isAuth) {
navigate('/');
}
}
return (
<section className="flex items-center h-screen">
<div className='bg-white container mx-auto flex flex-col p-10 max-w-2xl'>
<form onSubmit={handleSubmit(onSubmit)} className='bg-white container mx-auto flex flex-col p-10 max-w-2xl'>
<img className='w-52 self-center mb-5' src={ Logo } alt="logo" />
<h1 className='text-2xl font-semibold tracking-wider text-gray-800 capitalize '>Login to your account</h1>
<p className='mt-4 text-gray-500 mb-5'>Let's get you all set up so you can verify your personal account and begin setting up your profile.</p>
<div className="flex flex-col mb-5">
<label className="block text-sm text-gray-600 " htmlFor="email">Email:</label>
<input id='email' className='block w-full px-5 py-3 mt-2 text-gray-700 placeholder-gray-400 bg-white border border-gray-200 rounded-md focus:border-[#fbceb1] focus:ring-[#fbceb1] focus:outline-none focus:ring focus:ring-opacity-40' onChange={e => setEmail(e.target.value)} value={email} type='email' placeholder='Email'/>
<input id='email' className='block w-full px-5 py-3 mt-2 text-gray-700 placeholder-gray-400 bg-white border border-gray-200 rounded-md focus:border-[#fbceb1] focus:ring-[#fbceb1] focus:outline-none focus:ring focus:ring-opacity-40' {...register("email", { required: true })} placeholder='Email'/>
</div>
<div className="flex flex-col mb-5">
<label className="block text-sm text-gray-600 " htmlFor="password">Password:</label>
<input id='password' className='block w-full px-5 py-3 mt-2 text-gray-700 placeholder-gray-400 bg-white border border-gray-200 rounded-md focus:border-[#fbceb1] focus:ring-[#fbceb1] focus:outline-none focus:ring focus:ring-opacity-40' onChange={e => setPassword(e.target.value)} value={password} type='password' placeholder='Password'/>
<input id='password' className='block w-full px-5 py-3 mt-2 text-gray-700 placeholder-gray-400 bg-white border border-gray-200 rounded-md focus:border-[#fbceb1] focus:ring-[#fbceb1] focus:outline-none focus:ring focus:ring-opacity-40' {...register("password", { required: true })} placeholder='Password'/>
</div>
<button className='flex items-center justify-center mt-2 w-full px-6 py-3 text-sm tracking-wide text-white hover:text-[#fbceb1] capitalize transition-colors duration-300 transform bg-gray-800 rounded-md focus:outline-none focus:ring focus:ring-gray-300 focus:ring-opacity-50' onClick={() => store.login(email, password)}>Login</button>
</div>
<button className='flex items-center justify-center mt-2 w-full px-6 py-3 text-sm tracking-wide text-white hover:text-[#fbceb1] capitalize transition-colors duration-300 transform bg-gray-800 rounded-md focus:outline-none focus:ring focus:ring-gray-300 focus:ring-opacity-50'>Login</button>
<p className='mt-5 text-gray-700'>Dont have account? <Link className='text-[#fbceb1]' to={'/register'}>Sign up here.</Link></p>
</form>
</section>
)
}

@ -1,40 +1,68 @@
/* eslint-disable @typescript-eslint/no-misused-promises */
import {FC, useState, useContext} from 'react'
import {FC, useContext} from 'react'
import { useForm, SubmitHandler } from "react-hook-form";
import { useNavigate, Link } from 'react-router-dom';
import { Context } from '../main';
import Logo from '../assets/logo.png'
const RegisterForm: FC = () => {
const [email, setEmail] = useState<string>('');
const [name, setName] = useState<string>('');
const [sname, setSname] = useState<string>('');
const [password, setPassword] = useState<string>('');
type Form = {
email: string,
name: string,
sname: string,
password: string,
confirm_password: string
};
const navigate = useNavigate();
const {store} = useContext(Context);
const { register, handleSubmit, watch } = useForm<Form>();
const onSubmit: SubmitHandler<Form> = async (data) => {
await store.registration(data.email, data.name, data.sname, data.password);
if(store.isAuth) {
navigate('/');
}
}
return (
<section className="flex items-center h-screen">
<div className='bg-white container mx-auto flex flex-col p-10 max-w-2xl'>
<form onSubmit={handleSubmit(onSubmit)} className='bg-white container mx-auto flex flex-col p-10 max-w-2xl'>
<img className='w-52 self-center mb-5' src={ Logo } alt="logo" />
<h1 className='text-2xl font-semibold tracking-wider text-gray-800 capitalize '>Register To Get Started</h1>
<p className='mt-4 text-gray-500 mb-5'>Let's get you all set up so you can verify your personal account and begin setting up your profile.</p>
<div className="flex flex-col mb-5">
<label className="block text-sm text-gray-600 " htmlFor="email">Email:</label>
<input id='email' className='block w-full px-5 py-3 mt-2 text-gray-700 placeholder-gray-400 bg-white border border-gray-200 rounded-md focus:border-[#fbceb1] focus:ring-[#fbceb1] focus:outline-none focus:ring focus:ring-opacity-40' onChange={e => setEmail(e.target.value)} value={email} type='email' placeholder='Email'/>
<input id='email' className='block w-full px-5 py-3 mt-2 text-gray-700 placeholder-gray-400 bg-white border border-gray-200 rounded-md focus:border-[#fbceb1] focus:ring-[#fbceb1] focus:outline-none focus:ring focus:ring-opacity-40' {...register("email", { required: true })} type='email' placeholder='Email'/>
</div>
<div className="flex flex-col mb-5">
<label className="block text-sm text-gray-600 " htmlFor="name">First Name:</label>
<input id='name' className='block w-full px-5 py-3 mt-2 text-gray-700 placeholder-gray-400 bg-white border border-gray-200 rounded-md focus:border-[#fbceb1] focus:ring-[#fbceb1] focus:outline-none focus:ring focus:ring-opacity-40' onChange={e => setName(e.target.value)} value={name} type='text' placeholder='First Name'/>
<input id='name' className='block w-full px-5 py-3 mt-2 text-gray-700 placeholder-gray-400 bg-white border border-gray-200 rounded-md focus:border-[#fbceb1] focus:ring-[#fbceb1] focus:outline-none focus:ring focus:ring-opacity-40' {...register("name", { required: true })} placeholder='First Name'/>
</div>
<div className="flex flex-col mb-5">
<label className="block text-sm text-gray-600 " htmlFor="sname">Last Name:</label>
<input id='sname' className='block w-full px-5 py-3 mt-2 text-gray-700 placeholder-gray-400 bg-white border border-gray-200 rounded-md focus:border-[#fbceb1] focus:ring-[#fbceb1] focus:outline-none focus:ring focus:ring-opacity-40' onChange={e => setSname(e.target.value)} value={sname} type='text' placeholder='Last Name'/>
<input id='sname' className='block w-full px-5 py-3 mt-2 text-gray-700 placeholder-gray-400 bg-white border border-gray-200 rounded-md focus:border-[#fbceb1] focus:ring-[#fbceb1] focus:outline-none focus:ring focus:ring-opacity-40' {...register("sname", { required: true })} type='text' placeholder='Last Name'/>
</div>
<div className="flex flex-col mb-5">
<label className="block text-sm text-gray-600 " htmlFor="password">Password:</label>
<input id='password' className='block w-full px-5 py-3 mt-2 text-gray-700 placeholder-gray-400 bg-white border border-gray-200 rounded-md focus:border-[#fbceb1] focus:ring-[#fbceb1] focus:outline-none focus:ring focus:ring-opacity-40' onChange={e => setPassword(e.target.value)} value={password} type='password' placeholder='Password'/>
<input id='password' className='block w-full px-5 py-3 mt-2 text-gray-700 placeholder-gray-400 bg-white border border-gray-200 rounded-md focus:border-[#fbceb1] focus:ring-[#fbceb1] focus:outline-none focus:ring focus:ring-opacity-40' {...register("password", { required: true })} type='password' placeholder='Password'/>
</div>
<div className="flex flex-col mb-5">
<label className="block text-sm text-gray-600 " htmlFor="password">Confirm Password:</label>
<input id='password' className='block w-full px-5 py-3 mt-2 text-gray-700 placeholder-gray-400 bg-white border border-gray-200 rounded-md focus:border-[#fbceb1] focus:ring-[#fbceb1] focus:outline-none focus:ring focus:ring-opacity-40' {...register("confirm_password",
{
required: true,
validate: (val: string) => {
if (watch('password') != val) {
return "Your passwords do no match";
}
},
})} type='password' placeholder='Confirm Password'/>
</div>
<button className='flex items-center justify-center mt-2 w-full px-6 py-3 text-sm tracking-wide text-white hover:text-[#fbceb1] capitalize transition-colors duration-300 transform bg-gray-800 rounded-md focus:outline-none focus:ring focus:ring-gray-300 focus:ring-opacity-50' onClick={() => store.registration(email, name, sname, password)}>Register</button>
</div>
<button className='flex items-center justify-center mt-2 w-full px-6 py-3 text-sm tracking-wide text-white hover:text-[#fbceb1] capitalize transition-colors duration-300 transform bg-gray-800 rounded-md focus:outline-none focus:ring focus:ring-gray-300 focus:ring-opacity-50'>Register</button>
<p className='mt-5 text-gray-700'>Already have an account ? <Link className='text-[#fbceb1]' to={'/login'}>Log In here.</Link></p>
</form>
</section>
)
}

@ -1,6 +1,6 @@
import axios from "axios";
export const API_URL = `https://chess.beknazaryanstudio.ru:8080/api`
export const API_URL = `http://localhost:8089/api`
const $api = axios.create({
withCredentials: true,

@ -1,3 +1,7 @@
@tailwind base;
@tailwind components;
@tailwind utilities;
@tailwind utilities;
html {
font-family: 'Manrope', sans-serif;
}

@ -4,6 +4,7 @@ import App from './App.tsx'
import './index.css'
import Store from './store/store.ts'
interface State {
store: Store
}

@ -1,5 +1,8 @@
export interface User {
_id: string;
email: string;
role: string;
name: string;
sname: string;
avatar?: string;
role?: string;
}

@ -0,0 +1,13 @@
import { FC } from 'react'
import Sidebar from '../components/Index/Sidebar/Sidebar';
const IndexPage : FC = () => {
return (
<main className='flex h-screen'>
<Sidebar/>
</main>
)
}
export default IndexPage;

@ -1,10 +1,14 @@
import { User } from "../models/User"
import { makeAutoObservable } from "mobx";
import AuthService from "../services/AuthService";
import axios from "axios";
import { AuthResponse } from "../models/response/AuthResponse";
import { API_URL } from "../http";
export default class Store {
user = {} as User;
isAuth = false;
isLoading = true;
constructor() {
makeAutoObservable(this);
@ -18,10 +22,13 @@ export default class Store {
this.user = user;
}
setLoading(bool: boolean) {
this.isLoading = bool;
}
async registration (email: string, name: string, sname: string, password: string) {
try {
const response = await AuthService.registration(email, name, sname, password);
console.log(response);
localStorage.setItem('token', response.data.accessToken);
this.setAuth(true);
this.setUser(response.data.user)
@ -33,7 +40,6 @@ export default class Store {
async login (email: string, password: string) {
try {
const response = await AuthService.login(email, password);
console.log(response);
localStorage.setItem('token', response.data.accessToken);
this.setAuth(true);
this.setUser(response.data.user)
@ -52,4 +58,22 @@ export default class Store {
console.log(e)
}
}
async checkAuth() {
this.setLoading(true);
try {
const response = await axios.get<AuthResponse>(`${API_URL}/auth/refresh`, {withCredentials: true});
localStorage.setItem('token', response.data.accessToken);
this.setAuth(true);
this.setUser(response.data.user)
} catch (e) {
console.log(e);
} finally {
this.setLoading(false);
}
}
storeLoad() {
this.setLoading(false);
}
}

@ -7,4 +7,7 @@ export default defineConfig({
server: {
port: 3000,
},
preview: {
port: 3000,
}
})

@ -1,6 +1,6 @@
{
"serverPort": 8089,
"dbUrl": "db",
"dbUrl": "dbUrl",
"JWTAccessSecret": "jwt-ararat-access-sercet-dygqwuygoduwqygdqwugyid",
"JWTRefreshSecret": "jwt-ararat-refresh-sercet-dqwyugfuftyiqwdutyfivd"
}

@ -1,11 +1,17 @@
export default class UserDto {
_id;
email;
role
name;
sname;
role;
avatar;
constructor(model) {
this._id = model._id;
this.email = model.email;
this.name = model.name;
this.sname = model.sname;
this.role = model.role;
this.avatar = model.avatar;
}
}

@ -6,7 +6,7 @@ const User = new Schema({
sname: {type: String, required: true},
verify: {type: Boolean, default: false},
password: {type: String, required: true},
avatar: {type: String},
avatar: {type: String, default: '/avatar.svg'},
role: {type: String, default: "USER"},
});

Loading…
Cancel
Save