Skip to content

Commit

Permalink
[SOK-16]
Browse files Browse the repository at this point in the history
+add backendApi
+create axios interceptors
+add userReducer and create function for use yandex api
+add env
*update SignIn.tsx for use backendApi
*update SignUp.tsx for use backendApi
*format and lint
  • Loading branch information
gloginov committed Sep 27, 2024
1 parent 62af8ee commit 4110c13
Show file tree
Hide file tree
Showing 17 changed files with 383 additions and 59 deletions.
5 changes: 5 additions & 0 deletions .env.sample
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,8 @@ POSTGRES_USER=postgres
POSTGRES_PASSWORD=postgres
POSTGRES_DB=postgres
POSTGRES_PORT=5432

TRAEFIK_NETWORK_NAME=traefik_traefik
DOCKER_BUILDKIT=1 #Build only stages required for target
COMPOSE_FILE=docker-compose.yml:docker-compose.dev.yml
VITE_AUTH_URL='https://ya-praktikum.tech/api/v2'
6 changes: 0 additions & 6 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,6 @@
},
"devDependencies": {
"@evilmartians/lefthook": "^1.3.9",
"lefthook": "^1.7.15",
"lerna": "^5.4.3"
},
"dependencies": {
"normalize": "^0.3.1",
"react-router-dom": "^6.26.2",
"sass": "^1.79.1"
}
}
12 changes: 9 additions & 3 deletions packages/client/package.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"name": "Falcon-Tanks",
"name": "falcon-tanks",
"version": "1.0.0",
"type": "module",
"scripts": {
Expand All @@ -18,6 +18,9 @@
"react-dom": "^18.2.0"
},
"devDependencies": {
"react-redux": "^9.1.2",
"axios": "^1.7.7",
"@reduxjs/toolkit": "^2.2.7",
"@testing-library/react": "^13.3.0",
"@types/jest": "^28.1.8",
"@types/react": "^18.0.17",
Expand All @@ -28,11 +31,14 @@
"eslint": "^8.23.0",
"jest": "^28",
"jest-environment-jsdom": "^29.0.1",
"lefthook": "^1.3.9",
"lefthook": "^1.7.15",
"prettier": "^2.7.1",
"ts-jest": "^28.0.8",
"typescript": "^4.8.2",
"vite": "^3.0.7"
"vite": "^3.0.7",
"normalize": "^0.3.1",
"react-router-dom": "^6.26.2",
"sass": "^1.79.1"
},
"license": "MIT"
}
29 changes: 29 additions & 0 deletions packages/client/src/api/backendApi.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import axios, { AxiosError, AxiosResponse } from 'axios'

const instance = axios.create({
baseURL: import.meta.env.VITE_AUTH_URL,
headers: {
'Content-Type': 'application/json',
},
withCredentials: true,
})

instance.interceptors.response.use(
function (response: AxiosResponse) {
return response
},
function (error: AxiosError) {
// if app get response code 401 (died token), redirect user to sign-in form
if (error.response?.status === 401) {
localStorage.removeItem('user')
// save page where we get 401 and redirect after login
window.location.href = '/sign-in?redirectUrl=' + window.location.pathname
}

if (axios.isCancel(error)) return Promise.reject(error)

return Promise.reject(error)
}
)

export default instance
7 changes: 3 additions & 4 deletions packages/client/src/app/App.test.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import App from './App'
import { render, screen } from '@testing-library/react'
// import App from './App'
// import { render, screen } from '@testing-library/react'

// const appContent = 'Вот тут будет жить ваше приложение :)'

// @ts-ignore
// @ts-ignore @typescript-eslint/ban-ts-comment
global.fetch = jest.fn(() =>
Promise.resolve({ json: () => Promise.resolve('hey') })
)
Expand All @@ -13,6 +13,5 @@ test('Example test', async () => {
// expect(screen.getByText(appContent)).toBeDefined()
// const { getByAltText } = await render(<App />);
// const image = getByAltText('promoImage');
// @ts-ignore
// expect(image.src).toContain('FT-promo');
})
39 changes: 11 additions & 28 deletions packages/client/src/app/App.tsx
Original file line number Diff line number Diff line change
@@ -1,23 +1,22 @@
// import { useEffect } from 'react'

import '@/app/App.scss'
import AuthLayout from '@/layouts/auth-layout'
import { createBrowserRouter, RouterProvider } from 'react-router-dom'
import RootLayout from '@/layouts/root-layout'
import PrivateLayout from '@/layouts/private-layout'
import AuthLayout from '@/layouts/auth-layout'
import PublicLayout from '@/layouts/public-layout'
import RootLayout from '@/layouts/root-layout'
import { Error } from '@/pages/Error/Error'
import { Forum } from '@/pages/Forum/Forum'
import { Game } from '@/pages/Game/Game'
import { Leaderboard } from '@/pages/Leaderboard/Leaderboard'
import { Main } from '@/pages/Main/Main'
import { ChangePassword } from '@/pages/Profile/ChangePassword'
import { ProfileEdit } from '@/pages/Profile/Edit'
import { Profile } from '@/pages/Profile/Profile'
import { SignIn } from '@/pages/SignIn/SignIn'
import { SignUp } from '@/pages/SignUp/SignUp'
import { Game } from '@/pages/Game/Game'
import { Forum } from '@/pages/Forum/Forum'
import { Error } from '@/pages/Error/Error'
import { Thread } from '@/pages/Thread/Thread'
import '@/scss/styles.scss'
import { createBrowserRouter, RouterProvider } from 'react-router-dom'
import { Leaderboard } from '@/pages/Leaderboard/Leaderboard'
import { Profile } from '@/pages/Profile/Profile'
import { ProfileEdit } from '@/pages/Profile/Edit'
import { ChangePassword } from '@/pages/Profile/ChangePassword'

const routerConfig = createBrowserRouter([
{
Expand Down Expand Up @@ -84,23 +83,7 @@ const routerConfig = createBrowserRouter([
])

function App() {
// useEffect(() => {
// const fetchServerData = async () => {
// const url = `http://localhost:${__SERVER_PORT__}`
// const response = await fetch(url)
// const data = await response.json()
// console.log(data)
// }
//
// fetchServerData()
// }, [])
//

return (
<div className={'app-layout'}>
<RouterProvider router={routerConfig} />
</div>
)
return <RouterProvider router={routerConfig} />
}

export default App
35 changes: 24 additions & 11 deletions packages/client/src/components/ui/Button/Button.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,18 +3,31 @@ import './Button.scss'

export const Button = (props: {
text: string
className: string
useFixWidth: boolean
href?: string
className?: string | undefined
useFixWidth?: boolean | undefined
href?: string | undefined
onClick?: (() => Promise<void>) | (() => any) | undefined
}) => {
const { text, className, useFixWidth = false, href = '/' } = props
const { text, className, useFixWidth = false, href = '/', onClick } = props
return (
<Link
to={href}
className={`custom-button ${className} ${
useFixWidth ? 'custom-button_fix-width' : ''
}`}>
<span>{text}</span>
</Link>
<>
{onClick && typeof onClick === 'function' ? (
<button
className={`custom-button ${className} ${
useFixWidth ? 'custom-button_fix-width' : ''
}`}
onClick={onClick}>
<span>{text}</span>
</button>
) : (
<Link
to={href}
className={`custom-button ${className} ${
useFixWidth ? 'custom-button_fix-width' : ''
}`}>
<span>{text}</span>
</Link>
)}
</>
)
}
Empty file removed packages/client/src/index.css
Empty file.
19 changes: 18 additions & 1 deletion packages/client/src/layouts/private-layout.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,22 @@
import { useSelector } from 'react-redux'
import { Outlet } from 'react-router-dom'
import { RootState } from '@/store'
import { getUser, UserType } from '@/store/reducers/user-reducer'
import { useEffect } from 'react'
import { useAppDispatch } from '@/store'

export default function PrivateLayout() {
return <Outlet />
// get user from store
const user = useSelector<RootState, UserType>(state => state.UserReducer.user)
const dispatch = useAppDispatch()

useEffect(() => {
// if user is empty call backend
if (user === null) {
dispatch(getUser())
}
}, [user])

// if the user is full, show page
return user === null ? <h1>Загрузка...</h1> : <Outlet />
}
9 changes: 7 additions & 2 deletions packages/client/src/main.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,15 @@
import React from 'react'
import ReactDOM from 'react-dom/client'
import { Provider } from 'react-redux'
import { store } from '@/store'

import App from './app/App'
import './index.css'
import '@/scss/styles.scss'

ReactDOM.createRoot(document.getElementById('root') as HTMLElement).render(
<React.StrictMode>
<App />
<Provider store={store}>
<App />
</Provider>
</React.StrictMode>
)
9 changes: 8 additions & 1 deletion packages/client/src/pages/Game/Game.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,10 @@
import { Link } from 'react-router-dom'

export const Game = () => {
return <>Тут будет игра</>
return (
<>
Тут будет игра
<Link to={'/forum'}>Forum</Link>
</>
)
}
12 changes: 11 additions & 1 deletion packages/client/src/pages/Profile/Profile.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,13 @@
import { logoutUser } from '@/store/reducers/user-reducer'
import { useAppDispatch } from '@/store'

export const Profile = () => {
return <>Страница профиля</>
const dispatch = useAppDispatch()

return (
<>
Страница профиля
<button onClick={() => dispatch(logoutUser())}>Выйти</button>
</>
)
}
49 changes: 48 additions & 1 deletion packages/client/src/pages/SignIn/SignIn.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,50 @@
import { useState } from 'react'
import { useAppDispatch } from '@/store'
import { signInUser } from '@/store/reducers/user-reducer'
import { Button } from '@/components/ui/Button/Button'
import { useSearchParams } from 'react-router-dom'

export const SignIn = () => {
return <>Тут форма входа</>
const [form, setForm] = useState({ login: '', password: '' })
const [searchParams] = useSearchParams()
const [query] = useState(searchParams.get('redirectUrl'))
const dispatch = useAppDispatch()

const handleForm = (name: string, value: string) => {
setForm({ ...form, [name]: value })
}

const handleSubmit = () => dispatch(signInUser(form, query))

return (
<>
<form
onSubmit={e => {
e.preventDefault()
}}>
<label>
<span>Логин</span>
<input
onChange={e => handleForm('login', e.target.value)}
type="text"
name={'login'}
/>
</label>
<label>
<span>Пароль</span>
<input
onChange={e => handleForm('password', e.target.value)}
type="password"
name={'password'}
/>
</label>
<Button
text={'войти'}
useFixWidth={true}
onClick={() => handleSubmit()}
/>
</form>
<Button text={'Регистрация'} useFixWidth={true} href={'/sign-up'} />
</>
)
}
Loading

0 comments on commit 4110c13

Please sign in to comment.