This commit is contained in:
Primakov Alexandr Alexandrovich 2025-01-24 16:42:55 +03:00
parent 4b869ffe7a
commit 66af185358
14 changed files with 171 additions and 41 deletions

View File

@ -26,6 +26,6 @@ module.exports = {
},
},
config: {
"dry-wash.api.url": "/api"
"nav2.api": "/api"
},
}

View File

@ -1,4 +1,5 @@
import * as types from '../const'
import { starsSlice } from '../slices/stars'
import { setStars } from './stars'
@ -20,7 +21,7 @@ export const getUsers = () => async (dispatch, getState) => {
dispatch(success(data))
Object.keys(data).forEach(userId => {
dispatch(setStars({ id: userId, value: data[userId].rated }))
dispatch(starsSlice.actions.setStars({ id: userId, value: data[userId].rated }))
})
} else {
throw 'Что-то пошло не так'
@ -29,4 +30,4 @@ export const getUsers = () => async (dispatch, getState) => {
} catch (e) {
dispatch(error('Что-то пошло не так'))
}
}
}

View File

@ -0,0 +1,15 @@
export interface BaseResponse<Body> {
success: boolean;
body: Body;
}
export interface User {
id: string;
name: string;
age: number;
surname: null;
email: null;
rated: number;
avatar: string;
friends: User[];
}

View File

@ -47,4 +47,4 @@ export const reducer = createReducer(initialState, {
[types.USER_FETCH_SUCCESS]: fetchSuccess,
[types.USER_FETCH_ERROR]: fetchError,
[types.USER_FETCH_RESET]: reset,
})
})

View File

@ -0,0 +1,32 @@
import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react'
import { getConfigValue } from '@brojs/cli'
import { BaseResponse, User } from '../model'
export const api = createApi({
reducerPath: 'api',
baseQuery: fetchBaseQuery({
baseUrl: getConfigValue('nav2.api'),
headers: {
'Content-Type': 'application/json',
},
}),
tagTypes: ['Users'],
endpoints: (builder) => ({
users: builder.query<Record<string, User>, void>({
query: () => ({
url: '/users',
}),
providesTags: ['Users'],
transformResponse: (response: BaseResponse<User[]>) =>
response.body.reduce((acc, user) => ({ ...acc, [user.id]: user }), {}),
}),
updateRating: builder.mutation<void, { userId: string, rating: number }>({
query: ({ userId, rating }) => ({
url: `/user/rate/${userId}`,
method: 'POST',
body: { rating },
}),
invalidatesTags: ['Users'],
}),
}),
})

View File

@ -0,0 +1,23 @@
import { type PayloadAction, createSlice } from '@reduxjs/toolkit'
const initialState = {
friends: {} as Record<string, number>,
user: {},
}
export const starsSlice = createSlice({
name: 'stars',
initialState,
reducers: {
setStars(state, action) {
state.friends[action.payload.id] = action.payload.value
},
increment(state, action: PayloadAction<{ id: string }>) {
state.friends[action.payload.id] += 1
},
decrement(state, action) {
state.friends[action.payload.id] -= 1
},
},
})

View File

@ -1,18 +1,21 @@
import { configureStore } from '@reduxjs/toolkit'
import { useSelector } from 'react-redux'
import { starsReducer } from './reducers/stars'
import { reducer as usersFetchReducer } from './reducers/users'
import { starsSlice } from './slices/stars'
import { api } from './service/main'
export const store = configureStore({
reducer: {
stars: starsReducer,
user: usersFetchReducer,
[starsSlice.reducerPath]: starsSlice.reducer,
[api.reducerPath]: api.reducer,
},
devTools: {
name: 'stars',
},
middleware: (getDefaultMiddleware) => getDefaultMiddleware(),
middleware: (getDefaultMiddleware) => getDefaultMiddleware()
.concat(api.middleware),
})
export type StoreType = ReturnType<typeof store.getState>

View File

@ -2,11 +2,13 @@ import React, { useCallback, useState } from 'react'
import { BrowserRouter } from 'react-router-dom'
import { ChakraProvider } from '@chakra-ui/react'
import { Provider } from 'react-redux'
import { ApiProvider } from '@reduxjs/toolkit/query/react'
import { Dashboard } from './dashboard'
import { stars as starsContext } from './__data__/context'
import { users } from './__data__/users'
import { store } from './__data__/store'
import { api } from './__data__/service/main'
const App = () => {
const [stars, setStar] = useState(
@ -31,7 +33,9 @@ const App = () => {
<ChakraProvider>
<BrowserRouter>
<Provider store={store}>
{/* <ApiProvider api={api}> */}
<Dashboard />
{/* </ApiProvider> */}
</Provider>
</BrowserRouter>
</ChakraProvider>

View File

@ -22,15 +22,10 @@ import { stars as starsContext } from '../../__data__/context'
import { useUsers } from '../../hooks'
import { useDispatch, useSelector } from 'react-redux'
import { decrementStars, incrementStars } from '../../__data__/actions/stars'
type User = Record<string, unknown> & {
id: string
name: string
age: number
avatar?: string
rated: number
friends?: User[]
}
import { useAppSelector } from '../../__data__/store'
import { starsSlice } from '../../__data__/slices/stars'
import { User } from '../../__data__/model'
import { api } from '../../__data__/service/main'
const features = getFeatures('nav2')
@ -172,16 +167,22 @@ const Counter = ({
userId: string
}) => {
// const { rate, setUserRate } = useUsers((state) => state[userId].rated)
const rate = useSelector((store) => store.stars.friends[userId]) ?? 0
// const rate = useSelector((store) => store.stars.friends)
const { data, error, isLoading } = api.useUsersQuery()
const dispatch = useDispatch()
const handleIncrement = () => {
dispatch(starsSlice.actions.increment({ id: userId }))
}
return (
<Center>
<VStack>
<Heading>{rate}</Heading>
<Heading>{data?.[userId]?.rated}</Heading>
<Button onClick={() => dispatch(incrementStars({ id: userId }))}>+</Button>
<Button onClick={() => dispatch(decrementStars({ id: userId }))}>-</Button>
<Button onClick={handleIncrement}>+</Button>
<Button onClick={() => dispatch(starsSlice.actions.decrement({ id: userId }))}>-</Button>
</VStack>
</Center>
)

View File

@ -1,4 +1,4 @@
import { HStack, Icon } from '@chakra-ui/react'
import { HStack, Icon, transform } from '@chakra-ui/react'
import { FaRegStar, FaStar } from 'react-icons/fa6'
import React, { useMemo, useState } from 'react'
import { getConfigValue } from '@brojs/cli'
@ -7,6 +7,7 @@ import { useDispatch, useSelector } from 'react-redux'
import { stars } from '../__data__/context'
import { useUsers } from '../hooks'
import { setStars } from '../__data__/actions/stars'
import { api } from '../__data__/service/main'
const useStars = (currentRated, starsCount) => {
@ -39,11 +40,16 @@ export const Stars = ({
// setRated: starsSetRated
// } = useStars(rated, count)
// const { setUserRate } = useUsers(state => state[userId].rated)
const rate = useSelector((store) => store.stars.friends[userId]) ?? 0
// const rate = useSelector((store) => store.stars.friends[userId as '2']) ?? 0
const dispatch = useDispatch()
const { data, error, isLoading } = api.useUsersQuery(void 0)
const [updateRating, updateRatingRequest] = api.useUpdateRatingMutation()
const rate = data?.[userId]?.rated ?? 0
const handleStarsClick = (value: number) => {
dispatch(setStars({ id: userId, value }))
// dispatch(setStars({ id: userId, value }))
updateRating({ userId, rating: value })
// setUserRate(userId, stars)
// fetch(getConfigValue('dry-wash.api.url') + '/user-rate', {
// method: 'POST',

View File

@ -11,21 +11,23 @@ import { UnknownAction } from 'redux'
import { useAppSelector } from '../__data__/store'
import { Statuses } from '../__data__/reducers/users'
import { userSelectors } from '../__data__/selectors'
import { api } from '../__data__/service/main'
export const Friends = memo(() => {
// const [isLoading, setIsLoading] = useState(false)
// const [data, setData] = useState(null)
// const [error, setError] = useState(null)
const dispatch = useDispatch()
// const dispatch = useDispatch()
const [showModal, setShowModal] = useState(false)
const { data, error, isLoading } = api.useUsersQuery({})
const isLoading = useAppSelector(userSelectors.isLoading)
const data = useAppSelector(userSelectors.data)
const error = useAppSelector(userSelectors.error)
// const isLoading = useAppSelector(userSelectors.isLoading)
// const data = useAppSelector(userSelectors.data)
// const error = useAppSelector(userSelectors.error)
useEffect(() => {
dispatch(getUsers() as unknown as UnknownAction)
}, [])
// useEffect(() => {
// dispatch(getUsers() as unknown as UnknownAction)
// }, [])
if(!data || isLoading) return <h1>loading...</h1>

View File

@ -19,7 +19,6 @@ timer.fast = timer(300)
router.post('/user-rate', (req, res) => {
res.status(500).send({ ok: false })
})
router.use('/admin', router2)
router.get('/users',
@ -27,17 +26,18 @@ router.get('/users',
res.status(stubs.users.includes('error') ? 400 : 200).send(require(`../json/users/${stubs.users}.json`))
})
router.post('/user/rate/:userId', (req, res) => {
stubs.users = 'success-incremented'
res.send({ ok: true })
})
router2.get('/', (req, res) => {
res.send(`
<h2>Users</h2>
<ul>
<li><button onclick="fetch('/api/admin/users/success')" style="background-color: ${stubs.users === 'success' ? 'green' : '#ccc'}">success</button></li>
<li><button onclick="fetch('/api/admin/users/error')" style="background-color: ${stubs.users === 'error' ? 'green' : '#ccc'}">error</button></li>
</ul>
<h2>Users</h2>
<ul>
<li><button onclick="fetch('/api/admin/users/success')" style="background-color: ${stubs.users === 'success' ? 'green' : '#ccc'}">success</button></li>
<li><button onclick="fetch('/api/admin/users/success-incremented')" style="background-color: ${stubs.users === 'success-incremented' ? 'green' : '#ccc'}">success-incremented</button></li>
<li><button onclick="fetch('/api/admin/users/error')" style="background-color: ${stubs.users === 'error' ? 'green' : '#ccc'}">error</button></li>
</ul>
`)

View File

@ -0,0 +1,43 @@
{
"success": false,
"body": [
{
"id": "some-user-id",
"name": "alexandr",
"age": 38,
"surname": null,
"email": null,
"rated": 5,
"avatar": "https://www.gravatar.com/avatar/6529e885535ef67a3fad810ad71167c2c03f79480936e9b3a714731753cbb47e?d=robohash",
"friends": [
{
"id": "2",
"name": "not alexandr",
"surname": null,
"email": null,
"rated": 2,
"avatar": "https://www.gravatar.com/avatar/6e?d=robohash"
}
]
},
{
"id": "2",
"name": "not alexandr",
"surname": null,
"email": null,
"age": 24,
"rated": 2,
"avatar": "https://www.gravatar.com/avatar/6e?d=robohash",
"friends": [
{
"id": "some-user-id",
"name": "alexandr",
"surname": null,
"email": null,
"rated": 3,
"avatar": "https://www.gravatar.com/avatar/6529e885535ef67a3fad810ad71167c2c03f79480936e9b3a714731753cbb47e?d=robohash"
}
]
}
]
}

View File

@ -1,7 +1,7 @@
{
"success": false,
"body": {
"some-user-id": {
"body": [
{
"id": "some-user-id",
"name": "alexandr",
"age": 38,
@ -20,7 +20,7 @@
}
]
},
"2": {
{
"id": "2",
"name": "not alexandr",
"surname": null,
@ -39,5 +39,5 @@
}
]
}
}
]
}