Поправил фронтенд

This commit is contained in:
Александр Кирюхин 2024-01-21 01:46:45 +03:00
parent 61c7b04d34
commit b60dfdfb42
5 changed files with 78 additions and 60 deletions

View file

@ -1,16 +1,18 @@
import { Navigate, Route, RouterProvider, createBrowserRouter, createRoutesFromElements, useLocation, useRouteLoaderData } from 'react-router-dom' import { Navigate, Route, RouterProvider, createBrowserRouter, createRoutesFromElements, useLocation, useRouteLoaderData } from 'react-router-dom'
import PropTypes from 'prop-types' import PropTypes from 'prop-types'
import { UserProvider } from './store/user'
import { ajax } from './utils/fetch'
import Layout from './components/Layout' import Layout from './components/Layout'
import Index from './pages/Index' import Index from './pages/Index'
import Login from './pages/Login' import Login from './pages/Login'
import Register from './pages/Register' import Register from './pages/Register'
import NoMatch from './pages/NoMatch' import NoMatch from './pages/NoMatch'
import { UserProvider } from './store/user'
import { ajax } from './utils/fetch'
import Engine from './pages/Engine' import Engine from './pages/Engine'
import Quests from './pages/Quests' import Quests from './pages/Quests'
import User from './pages/User'
const router = createBrowserRouter( const router = createBrowserRouter(
createRoutesFromElements( createRoutesFromElements(
@ -30,6 +32,7 @@ const router = createBrowserRouter(
element={<Auth><Quests /></Auth>} element={<Auth><Quests /></Auth>}
loader={() => ajax('/api/games').catch(x => { console.log(x); return null })} loader={() => ajax('/api/games').catch(x => { console.log(x); return null })}
/> />
<Route path="me" element={<User />} />
<Route path="login" element={<Login />} /> <Route path="login" element={<Login />} />
<Route path="register" element={<Register />} /> <Route path="register" element={<Register />} />
<Route <Route

View file

@ -3,10 +3,11 @@
padding-top: 0 !important; padding-top: 0 !important;
padding-bottom: 0 !important; padding-bottom: 0 !important;
height: 40px; height: 40px;
width: 85px; width: 135px;
background-image: url("./logo_small.png"); background-image: url("./logo_small.png");
background-size: 100%; background-size: cover;
display: inline-block; display: inline-block;
margin-right: 50px;
} }
th.thin { th.thin {

View file

@ -1,9 +1,9 @@
import { useEffect } from 'react'
import { Link, Outlet, useLoaderData, useLocation, useNavigate } from 'react-router-dom' import { Link, Outlet, useLoaderData, useLocation, useNavigate } from 'react-router-dom'
import { UserProvider } from '../store/user' import { UserProvider } from '../store/user'
import { useEffect } from 'react'
import { ajax } from '../utils/fetch' import { ajax } from '../utils/fetch'
import { Layout, Menu, Progress, Space } from 'antd' import { Layout, Menu } from 'antd'
import { FireTwoTone, StarTwoTone } from '@ant-design/icons' import { MenuOutlined } from '@ant-design/icons'
import { Content, Header } from 'antd/es/layout/layout' import { Content, Header } from 'antd/es/layout/layout'
const AppLayout = () => { const AppLayout = () => {
@ -34,65 +34,32 @@ const AppLayout = () => {
link: '/quests' link: '/quests'
}, },
{ {
key: 'user', key: 'me',
label: user.username, label: `${user.username} [${user.level}]`,
children: [ link: '/me'
{
type: 'group',
key: 'exp',
label: (
<Space>
<Space title={`${user.level} уровень`}><StarTwoTone />{user.level} уровень</Space>
<Space title={`${user.experience} опыта`}><FireTwoTone />{user.experience} опыт</Space>
</Space>)
},
{
type: 'group',
key: 'progress',
label: <Progress
value={user.experience}
percent={((user.experience - user.expToCurrentLevel) / (user.expToNextLevel - user.expToCurrentLevel) * 100)}
size="small"
showInfo={false}
/>
},
{
type: 'group',
key: 'nextLevel',
label: `Следующий уровень - ${user.expToNextLevel} очков опыта`
}, },
{ {
key: 'logout', key: 'logout',
label: 'Выход', label: 'Выход',
handler: logout onClick: logout
}
]
} }
] ]
} }
const menuHandler = (x) => { const menuHandler = (x) => {
const item = getItemByPath(items, x.keyPath) const item = items.find(y => y.key === x.key)
if (item.link) { navigate(item.link) } if (item.link) { navigate(item.link) }
if (item.handler) { item.handler() }
}
const getItemByPath = (items, path) => {
const x = path.pop()
const item = items.find(y => y.key === x)
if (path.length === 0) {
return item
}
return getItemByPath(item.children, path)
} }
return (<Layout> return (<Layout>
<Header style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between' }}> <Header style={{ display: 'flex', alignItems: 'center', width: '100%', justifyContent: 'space-between' }}>
<Link to="/" className="navbar-brand"></Link> <Link to="/" className="navbar-brand"></Link>
<Menu <Menu
mode="horizontal" mode="horizontal"
items={items} items={items}
selectedKeys={location.pathname.replace('/', '')} selectedKeys={location.pathname.replace('/', '')}
onClick={menuHandler} onClick={menuHandler}
overflowedIndicator={<MenuOutlined />}
style={{ width: '100%' }}
/> />
</Header> </Header>
<Content style={{ padding: '0 24px' }}> <Content style={{ padding: '0 24px' }}>

View file

@ -2,10 +2,10 @@ import { useLoaderData, useNavigate } from 'react-router-dom'
import Markdown from 'react-markdown' import Markdown from 'react-markdown'
import remarkGfm from 'remark-gfm' import remarkGfm from 'remark-gfm'
import moment from 'moment/min/moment-with-locales' import moment from 'moment/min/moment-with-locales'
import { Avatar, Typography, Button, Space, Card } from 'antd' import { Avatar, Typography, Button, Space, Card, Popover } from 'antd'
import { UserProvider } from '../store/user' import { UserProvider } from '../store/user'
const { Title } = Typography const { Title, Paragraph } = Typography
const Quests = () => { const Quests = () => {
moment.locale('ru') moment.locale('ru')
@ -22,10 +22,25 @@ const Quests = () => {
const renderItem = (user, navigate, item) => { const renderItem = (user, navigate, item) => {
const actions = [ const actions = [
<Space key='exp' title={`${item.points} опыта за выполнение квеста`}>Оп: {item.points}</Space>, <Popover
<Space key='taskCount' title={`${item.taskCount} уровней в квесте`}>Ур: {item.taskCount}</Space>, key='type'
content={<Paragraph>Квесты бывают двух типов: <ul><li><b>Виртуальные</b> - проходимые из дома</li><li><b>Полевые</b> - проходимые в городе Казани</li></ul></Paragraph>}
title="Тип квеста">
<Space>
{item.type === 'city' ? 'Полевой' : 'Виртуальный'}
</Space>
</Popover>,
<Popover
key='exp'
content={<Paragraph>Вы получите {item.points} очков опыта за выполнение этого квеста.<br />Чем сложнее квест - тем больше за него опыта!</Paragraph>}
title='Опыт за выполнения квеста'>
<Space>{item.points} ОО</Space>
</Popover>,
<Popover key='taskCount' content={`Этот квест состоит из ${item.taskCount} уровней`} title='Количество уровней в квесте'>
<Space>{item.taskCount} ур</Space>
</Popover>,
<>{moment(item.createdAt).fromNow()}</>, <>{moment(item.createdAt).fromNow()}</>,
<>Автор(ы) {item.authors.map(a => a.username)}</> <>Автор {item.authors.map(a => a.username)}</>
] ]
let questAction = (<span>Необходимо войти</span>) let questAction = (<span>Необходимо войти</span>)
@ -41,11 +56,9 @@ const renderItem = (user, navigate, item) => {
<Card <Card
key={item.id} key={item.id}
actions={actions} actions={actions}
title={item.title} title={<><Avatar src={`/api/file/${item.icon}`} />&nbsp;{item.title}</>}
style={{ marginBottom: 8 }} style={{ marginBottom: 8 }}
> >
<Avatar src={`/api/file/${item.icon}`} style={{ float: 'left', margin: '4px' }} />
<Markdown <Markdown
remarkPlugins={[remarkGfm]}> remarkPlugins={[remarkGfm]}>
{item.description} {item.description}

View file

@ -0,0 +1,34 @@
import { Avatar, Popover, Progress, Space, Typography } from 'antd'
import { UserProvider } from '../store/user'
import Markdown from 'react-markdown'
const { Paragraph } = Typography
const User = () => {
const { user } = UserProvider.useContainer()
if (user == null) {
return (<Space>Загрузка...</Space>)
}
return (<>
<h1>{user.username}</h1>
<Paragraph>Уровень: {user.level} ур</Paragraph>
<Paragraph>Очков опыта: {user.experience} ОО</Paragraph>
<Paragraph>Следующий уровень: {user.expToNextLevel} ОО</Paragraph>
<Progress
value={user.experience}
percent={((user.experience - user.expToCurrentLevel) / (user.expToNextLevel - user.expToCurrentLevel) * 100)}
size="small"
showInfo={false}
/>
{user.games.map(item => <Popover
key={item.id}
title={item.title}
content={<Markdown>{item.description}</Markdown>}
>
<Avatar size={64} style={{ marginRight: 4, marginBottom: 4 }} key={item.id} src={`/api/file/${item.icon}`} />
</Popover>)}
</>)
}
export default User